前回まででセミナー情報の登録と、ユーザーの登録・ログインは実装できたので、ようやく本題のセミナー受講申込機能を付けたいと思います。
まずは申請情報のモデルです。
from django.db import models
from django.contrib.auth import get_user_model#追加
from django.utils import timezone#追加
class Seminar(models.Model):
TYPE_CHOICES = [
('el', 'eラーニング'),
('f2f', '集合研修'),
]
name=models.CharField("セミナー名", max_length=200)
type=models.CharField("形式", max_length=3,choices=TYPE_CHOICES)
accept_start_date=models.DateField("申請受付開始日", )
accept_end_date=models.DateField("申請受付終了日", null=True, blank=True,)
start_date=models.DateField("開催日(開始日)", )
end_date=models.DateField("開催日(終了日)", null=True, blank=True,)
overview=models.TextField("概要", )
#追加
class Application(models.Model):
applicant=models.ForeignKey(get_user_model(), on_delete=models.CASCADE, verbose_name="申請者")
seminar=models.ForeignKey(Seminar, on_delete=models.CASCADE, verbose_name="受講セミナー")
approved=models.BooleanField("承認状況", default=False,)
applied_time=models.DateTimeField("申請日時", null=False,blank=True,default=timezone.now)
approved_time=models.DateTimeField("承認日時", null=True,blank=True,)
続いてフォームを検討します。
ただ、すでにトップ画面にセミナー一覧が表示されているのでそこに申込ボタンを付けるようにしたいと思います。そう考えたとき、特に何かユーザー側で入力してもらう必要はないですよね。
なので最低限の情報をhidden属性で保持して、申込の確認画面を表示したいと思います。
フォームは下記の通りになります。
from django import forms
from .models import Seminar, Application#追加
from tinymce.widgets import TinyMCE
class DatePickWidget(forms.DateInput):
input_type="date"
class SeminarForm(forms.ModelForm):
class Meta:
model = Seminar
fields = (
"name",
"type",
"accept_start_date",
"accept_end_date",
"start_date",
"end_date",
"overview")
widgets={
"accept_start_date":DatePickWidget,
"accept_end_date":DatePickWidget,
"start_date":DatePickWidget,
"end_date":DatePickWidget,
"overview":TinyMCE,
}
#追加
class ApplicationForm(forms.ModelForm):
class Meta:
model =Application
fields= (
"applicant",
"seminar",
)
widgets={
"applicant":forms.HiddenInput(),
"seminar":forms.HiddenInput(),
}
項目は申請者と申し込むユーザーのみで、それぞれウィジェットにhiddeninputを指定しています。
from django.shortcuts import render
from django.views.generic import ListView, CreateView
from .form import SeminarForm,ApplicationForm
from .models import Seminar
#以下追加
from django.shortcuts import get_object_or_404
from django.contrib.auth.mixins import LoginRequiredMixin,UserPassesTestMixin
class index(ListView):
model=Seminar
template_name = "application/index.html"
#UserPassesTestMixin追加
class program_add(UserPassesTestMixin,CreateView):
form_class = SeminarForm
template_name="application/program_add.html"
success_url="/"
#追加
def test_func(self):
return self.request.user.is_staff
#追加
class application_add(LoginRequiredMixin,CreateView):
form_class=ApplicationForm
template_name="application/application_add.html"
success_url="/"
def get_initial(self):
initial=super().get_initial()
initial.update({"applicant":self.request.user})
initial.update({"seminar":get_object_or_404(Seminar, pk=self.kwargs.get('seminar'))})
return initial
続いてビューを見ていきましょう。
application_addが追加されていますが、これまでのcreateviewと異なるのは、loginrequiredmixinとget_initialですね。
クラスの継承の際にこのLoginRequiredMixinを追加するとアクセスした際にログインが求められます。(ログイン済みの場合はそのまま進めます。)フォームでユーザー情報が必要なためここではログインが必須ですね。
get_initialはフォームの初期値を設定するための関数です。継承元の処理をsuper()で実行してから、最後に辞書のinitialを返すと、その内容がフォームのvalueとして予めセットされます。
ここではapplicantにはログインユーザー情報を入れ、seminarにはurlのパラメーターから受け取った情報からseminarオブジェクトを取得して入れています。
また、ついでに以前作成したprogram_addにUserPassesTestMixinを追加しています。セミナー情報の追加はスタッフ権限を持っている人しか出来ないようにするべきなので、そのようにしています。このmixinを継承するとtest_func関数が実行され、falseが返るとforbiddenが表示されます。そのためログインユーザのis_staffを返せば、スタッフのみがアクセスできるようになります。
{% extends 'base/base.html' %}
{% load crispy_forms_tags %}
{%block content%}
下記のセミナーを申し込みますが、よろしいですか?
<table class="table bordered">
<tr><td>セミナー名</td><td>{{form.seminar.initial.name}}</td></tr>
<tr><td>受講者名</td><td>{{form.applicant.initial}}</td></tr>
</table>
{{ form.media }}
<form action="" method="POST">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">申込</button>
</form>
{%endblock%}
application_addのテンプレートは一応確認画面の体にするため、表形式でセミナーと受講者を表示していますが、それ以外は他のフォームと同じです。
from django.urls import path
from .views import program_add,application_add
app_name='application'
urlpatterns = [
path('program/add/', program_add.as_view(),name='program_add'),
path('application/add/<int:seminar>', application_add.as_view(),name='application_add'),#追加
]
urls.pyはこのようになります。<int:seminar>と記載されていますが、ここは数字にマッチングして、その数字をkwargsにseminarをキーとして保存してくれます。先ほどのビューではこれを利用しています。
あとはセミナーの一覧にこのurlに飛ばすボタンを作成するだけですね。
{% extends 'base/base.html' %}
{%block content%}
<h1>セミナー申込み</h1>
<a href={%url 'application:program_add' %} class='btn btn-primary m-1'>追加</a>
{% for seminar in object_list %}
<table class='table table-bordered'>
<tr><td class='col-3'>セミナー名</td><td class='col-9'>{{ seminar.name }}</td></tr>
<tr><td>開催形式</td><td>{{ seminar.get_type_display }}</td></tr>
<tr><td>開催日</td><td>{{ seminar.start_date }}{%if seminar.end_date %}~{{ seminar.end_date }}{%endif%}</td></tr>
<tr><td>概要</td><td>{{ seminar.overview|safe }}</td></tr>
</table>
<a class="btn btn-primary" href="{%url 'application:application_add' seminar.pk %}">申込</a>
{% endfor %}
{%endblock%}
最後の部分に申込のボタンを追加しました。
urlタグではパスの名前を指定したあとに変数を記載すると、先ほどの<int>等の部分に順番に値を入れたurlを作ってくれます。
これで申込の機能が完成しました。実際に試してみて管理画面から登録できているか確認してみましょう。