やりたいこと
メールアドレス再入力の項目を作成し、連絡先(後でラベルは変更)=再入力メールアドレスなら内容を保存し、successful.htmlにリダイレクト。同じではなければ、このページにリダイレクトするが、すでに入力した項目が消えるとUXが下がるので残るようにする。
送信前
保存失敗後のリダイレクト画面
前提
#models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth import get_user_model
# Create your models here.
User = get_user_model()
class Reservation(models.Model):
lang_select = (
("Japanese", "日本語"),
("English", "English"),
("SimpleChinese", "簡体"),
("TraditionalChinese", "繁体"),
)
purpose_select = (
("reservation", "お店の予約"),
("cancel", "予約のキャンセル"),
)
request_option_select = (
("agent", "電話予約の代行"),
("confirm", "行きたい日に空いているか確認"),
("adjust", "予約いっぱいでも調整可能か確認"),
("tell", "事前に来店することを伝える"),
("ask", "店舗に聞きたいことがある"),
("tell_cancel", "予約のキャンセル(店舗予約前)"),
("book_cancel", "予約のキャンセル(店舗予約済み)"),
("other", "その他"),
)
request_status_select = (
("ongoing", "予約待ち"),
("complete", "完了"),
)
#ユーザー情報
date = models.DateTimeField(verbose_name="*依頼日", auto_now_add=True)
user_id = models.CharField(verbose_name="*ユーザーID", max_length=10000, blank=True, null=True)
username = models.CharField(verbose_name="*予約名",max_length=15)
email = models.EmailField(verbose_name="*連絡先")
lang = models.CharField(verbose_name="*使用言語",choices=lang_select,max_length=50)
#店舗情報
place = models.CharField(verbose_name="*店舗名", max_length=150)
call_num = models.CharField(verbose_name="*店舗電話番号", max_length=15)
address = models.CharField(verbose_name="店舗住所", max_length=200, blank=True, null=True)
urls = models.URLField(verbose_name="店舗URL", max_length=3000)
#依頼内容
purpose = models.CharField(verbose_name="*利用目的", choices=purpose_select,max_length=50)
request_option = models.CharField(verbose_name="*依頼内容", choices=request_option_select,max_length=50)
first_date = models.DateTimeField(verbose_name="*第一予約希望日")
second_date = models.DateTimeField(verbose_name="第二予約希望日", blank=True, null=True)
third_date = models.DateTimeField(verbose_name="第三予約希望日", blank=True, null=True)
content = models.TextField(verbose_name="その他希望事項", max_length=1000, blank=True, null=True)
#management用
request_status = models.CharField(verbose_name="依頼状況", choices=request_status_select,max_length=50)
class Meta:
db_table = "Reservations"
def __str__(self):
return self.email
#urls.py
from django.urls import path
from . import views
app_name = 'reservations'
urlpatterns = [
path('', views.reservation, name="reservation"),
path('successful/', views.successful, name="successful"),
]
confirmed_mailを追加し、field_orderでフォームの表示順序を変える。ここではemailの後にcomfirmed_mailが表示されるようにする。
#forms.py
from django import forms
from .models import Reservation
from bootstrap_datepicker_plus.widgets import DateTimePickerInput
from django.contrib import messages
class ReservationForm(forms.ModelForm):
confirmed_mail = forms.EmailField(label='*メールアドレス再入力')
field_order = ['username', 'email', 'confirmed_mail', 'lang', 'place', 'call_num', "address", "urls", 'purpose', 'request_option','first_date', 'second_date', 'third_date', 'content']
class Meta:
model = Reservation
fields = ['username', 'email', 'lang', 'place', 'call_num', "address", "urls", 'purpose', 'request_option','first_date', 'second_date', 'third_date', 'content']
exclude = ["request_status", "user_id"]
widgets = {
'username': forms.TextInput(attrs={'required': True}),
'email': forms.EmailInput(attrs={'required': True}),
'lang': forms.Select(attrs={'required': True}),
'place':forms.TextInput(attrs={'required': True}),
'call_num':forms.TextInput(attrs={'required': True}),
'address':forms.TextInput(attrs={'required': True}),
'purpose':forms.Select(attrs={'required': True}),
'request_option':forms.Select(attrs={'required': True}),
'content':forms.Textarea(attrs={'cols': '40', 'rows': '10'}),
'first_date': DateTimePickerInput(
attrs={'required': True},
options={
'locale': 'ja',
'dayViewHeaderFormat': 'YYYY年 MMMM',
}
),
'second_date': DateTimePickerInput(
options={
'locale': 'ja',
'dayViewHeaderFormat': 'YYYY年 MMMM',
}
),
'third_date': DateTimePickerInput(
options={
'locale': 'ja',
'dayViewHeaderFormat': 'YYYY年 MMMM',
}
),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields.values():
field.widget.attrs['class'] = 'form-control col-12'
field.required = False
views.pyで入力の確認と、リダイレクト後の項目自動入力を行う
get_initial関数でフォーム画面が表示された時に自動入力する項目を指定する。ここではログインしている場合はその項目を、リダイレクトされた場合は、前の入力項目を自動で入れておく。
メールがあっているかの確認はpost関数で行う。self.request.session[‘form_data’] = request.POSTで入力項目の内容を一時的に保存する。
form.save()の後は次回新規入力の時に入力が自動でされていたら、今度は消さないといけないので、self.request.session[‘form_data’] = Noneで消しておく。
messages.add_message(self.request, messages.WARNING, “メールアドレスが一致しません”)でエラーメッセージを表示させる。こちらはHTMLでも設定する必要がある。
#views.py
from django.shortcuts import render
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.mixins import UserPassesTestMixin
from django.views.generic import TemplateView, View
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from django.views.generic.list import ListView
from django.views.generic.detail import DetailView
from django.urls import reverse_lazy
from django.shortcuts import redirect
from django.contrib import messages
from django.core.exceptions import ValidationError
from .models import Reservation
from .forms import ReservationForm
# Create your views here.
#-------------------------------------------------
class ReservationView(CreateView):
template_name = "reservations/reservation.html"
model = Reservation
form_class = ReservationForm
success_url = reverse_lazy("reservations:successful")
def get_initial(self):
initial = super().get_initial()
form_data = self.request.session.get('form_data')
if form_data:
initial.update(form_data)
elif self.request.user.is_authenticated:
initial['username'] = self.request.user.username
initial['email'] = self.request.user.email
initial['lang'] = self.request.user.lang
return initial
def post(self, request, *args, **kwargs):
form = ReservationForm(request.POST)
if request.POST.get("email") != request.POST.get("confirmed_mail"):
self.request.session['form_data'] = request.POST
messages.add_message(self.request, messages.WARNING, "メールアドレスが一致しません")
return redirect('reservations:reservation')
elif form.is_valid():
form = form.save(commit=False)
form.request_status = "予約待ち"
if self.request.user.is_authenticated:
form.user_id = self.request.user.id
form.save()
self.request.session['form_data'] = None
return redirect('reservations:successful')
else:
self.request.session['form_data'] = request.POST
messages.add_message(self.request, messages.WARNING, "予約に失敗しました")
return redirect('reservations:reservation')
reservation = ReservationView.as_view()
class SuccessfulView(TemplateView):
template_name = "reservations/successful.html"
successful = SuccessfulView.as_view()
bootstrapを使用していれば{% if messages %}の中身はコピペでも良いと思う。
<!-- reservation.html-->
{% extends "base.html" %}
{% load static %}
{% block title %}Reservations{% endblock %}
{% block extracss %}
<link href="{% static 'css/mystyles.css' %}" rel="stylesheet" />
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<link href="/static/bootstrap_datepicker_plus/css/datepicker-widget.css" type="text/css" media="all" rel="stylesheet">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.9.0/moment-with-locales.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/js/bootstrap-datetimepicker.min.js"></script>
<script type="text/javascript" src="/static/bootstrap_datepicker_plus/js/datepicker-widget.js"></script>
{% endblock %}
{% block main %}
<body style="margin: 0; padding: 0; font-family: Arial, sans-serif; background-color: #f2f2f2;">
{% if messages %}
<div class="container">
<div class="row">
<div class="my-div-style w-100">
<ul class="messages" style="list-style: none;">
{% for message in messages %}
<li {% if message.tags %} class="{{ message.tags }}">
{% endif %}
{{ message }}
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
{% endif %}
<div class="container font-family: Arial, sans-serif; background-color: #f2f2f2;">
<div class="row">
<div class="col-lg-3"></div>
<div class="col-12 col-lg-6">
<div class="custom-container">
{{ form.media }}
<form class="form" action="" method="POST">
{% csrf_token %}
<h2>Reservation</h2>
{% for fields in form %}
<div class="form-group">
<label for="">{{ fields.label_tag }}</label>
{{ fields }}
{% if field.help_text %}
<span class="helptext">{{ field.help_text }}</span>
{% endif %}
{{ field.errors }}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">Reserve</button>
</form>
<div class="signup mt-5">
<p>Don't have an account yet? <a href="{% url 'accounts:signup' %}">Sign Up</a></p>
</div>
</div>
<div class="mb-5 text-center">
<button type="button" class="btn btn-default" style="margin-left:50%;" onclick="history.back()">戻る</button>
</div>
</div>
<div class="col-lg-3"></div>
</div>
</div>
</body>
{% endblock %}