はじめに
Djangoで実装した登録、更新、検索フォームに以下のようなカレンダコントロールを設置する方法をまとめます。
django-bootstrap-datepicker-plus
(現最新バージョン3.0.5)を使用します。
github.com
前提
- 登録、更新フォームは
django.forms.ModelForm
で実装していること
- 検索フォームは
django-filters.FilterSet
で実装していること
サンプルアプリの実装
まずは説明に用いるサンプルアプリ(カレンダコントロールなし)を実装していきます。
実行環境は以下の通りです。本エントリの主題ではないため使用する各ライブラリの詳細な説明は省略します。
- django==3.0.8 ※ Django 2でも良いです。
- django-bootstrap4==2.2.0 ※ Bootstrap3でも良いです。
- django-crispy-forms==1.9.2 ※ フォームの描画に使います。
- django-filter==2.3.0 ※ 検索機能の実装に使います。
INSTALLED_APPS = [
'crispy_forms',
'django_filters',
'bootstrap4',
]
CRISPY_TEMPLATE_PACK = 'bootstrap4'
アプリの作成
アプリ app
を作成します。
$ python manage.py startapp app
settings.py
の INSTALLED_APPS
に app
を追加します。
INSTALLED_APPS = [
'app',
]
モデル
MyModel
モデルに日付項目 date_field
と日時項目 datetime_field
を用意します。
from django.db import models
class MyModel(models.Model):
date_field = models.DateField()
datetime_field = models.DateTimeField()
DBにテーブルを作成します。
$ python manage.py makemigrations
$ python manage.py migrate
ビューとURL
クラスベースの汎用ビューを使用します。
CreateViewとUpdateViewはDjango標準のビューですが、 FilterViewは django_filters
のビューです。
from django_filters.views import FilterView
from django.urls import path, reverse_lazy
from django.views.generic.edit import CreateView, UpdateView
from app.forms import MyModelForm
from app.filters import MyModelFilter
from app.models import MyModel
app_name = 'app'
urlpatterns = [
path('my-model/new',
CreateView.as_view(
form_class=MyModelForm,
template_name='app/create_update.html',
success_url=reverse_lazy('app:search'),
),
name='create'),
path('my-model/<int:pk>',
UpdateView.as_view(
model=MyModel,
form_class=MyModelForm,
template_name='app/create_update.html',
success_url=reverse_lazy('app:search'),
),
name='update'),
path('my-model/search',
FilterView.as_view(
filterset_class=MyModelFilter,
template_name='app/search.html',
),
name='search'),
]
/app
のURLでアクセスできるように my_project/urls.py
に app.urls
を登録します。
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('admin/', admin.site.urls),
path('app/', include('app.urls')),
]
モデルフォーム
登録ビュー( app:create
)と更新ビュー( app:update
)の form_class
に指定した MyModelForm
の実装です。
このクラスに後ほどカレンダコントロールを組み込みますが、まずは単純に MyModel
の全フィールドを指定するだけです。
from django import forms
from app.models import MyModel
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ['date_field', 'datetime_field']
フィルタ
検索ビュー( app:search
)の filterset_class
に指定した MyModelFilter
の実装です。
MyModel
の全フィールドを対象に完全一致検索のフィルタを実装しています。
こちらもモデルフォームと同様に後ほど修正対象となります。
import django_filters
from app.models import MyModel
class MyModelFilter(django_filters.FilterSet):
class Meta:
model = MyModel
fields = ['date_field', 'datetime_field']
テンプレート
ベーステンプレート
各テンプレートで共通する箇所を app/templates/app/base.html
に実装します。
django-bootstrap4
でBootstrap4を読み込み、各テンプレートで実装する extrahead
ブロックと content
ブロックを用意しています。
<html>
<head>
{% load bootstrap4 %}
{% bootstrap_css %}
{% bootstrap_javascript jquery='full' %}
{% block extrahead %}{% endblock %}
</head>
<body>
<div class="container">
{% block content %}{% endblock %}
</div>
</body>
</html>
登録/更新画面のテンプレート
登録画面と更新画面は全く同じ実装になるため、いずれも app/templates/app/create_update.html
を使用します。
base.html
を継承し、 content
ブロックにフォームを実装します。
CreateView
と UpdateView
がコンテキストとして返す form
を django-crispy-forms
のcrispyフィルタで描画します。
{% extends 'app/base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<form method="post" class="form">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
{% endblock %}
登録画面の動作確認
/app/my-model/new
で MyModel
のフォームが表示されます。
Submitボタンの押下で新規データが登録、検索画面にリダイレクトされることを確認します。
更新画面の動作確認
/app/my-model/<int:pk>
でURLパラメタ pk
に指定したPK値のモデルオブジェクトが表示されます。
以下の例では date_field
の値を 2020-01-01
から 2020-01-03
に変更しています。
検索画面のテンプレート
登録/更新画面のテンプレートとの差異は以下の通りです。
<form>
の method
に get
を指定
- フォームを
filter.form
で参照
- フォーム以下に検索結果(
filter.qs
)を表示
{% extends 'app/base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<form method="get" class="form">
{% csrf_token %}
{{ filter.form|crispy }}
<button type="submit" class="btn btn-primary">Search</button>
</form>
{% for obj in filter.qs %}
date_field: {{ obj.date_field }} datetime_field: {{ obj.datetime_field }}<br/>
{% endfor %}
{% endblock %}
検索画面の動作確認
date_field
が 2020-01-01
であるデータを検索しています。
カレンダコントロールの導入
ここまででようやくサンプルアプリの実装が完了したので、いよいよカレンダコントロールを設置していきます。
django-bootstrap-datepicker-plusのインストール
pipでインストールし、 INSTALLED_APPS
に bootstrap_datepicker_plus
を追加します。
$ pip install django-bootstrap-datepicker-plus
INSTALLED_APPS = [
...
'bootstrap_datepicker_plus',
]
登録/更新画面
モデルフォームの修正
次の修正を加えます。
bootstrap_datepicker_plus
をインポート
Meta.widgets
の追加
- 日付項目(
DateField
)の場合は datetimepicker.DatePickerInput
を指定
- 日時項目(
DateTimeField
)の場合は datetimepicker.DateTimePickerInput
を指定
DatePickerInput
と DateTimePickerInput
の format
はPythonのdatetimeフォーマットを指定します。
options
はJavaScriptの datepicker
インスタンスに渡されるので、Bootstrap Datepicker Options Referenceを指定できます。
import bootstrap_datepicker_plus as datetimepicker
class MyModelForm(forms.ModelForm):
class Meta:
widgets = {
'date_field': datetimepicker.DatePickerInput(
format='%Y-%m-%d',
options={
'locale': 'ja',
'dayViewHeaderFormat': 'YYYY年 MMMM',
}
),
'datetime_field': datetimepicker.DateTimePickerInput(
format='%Y-%m-%d %H:%M:%S',
options={
'locale': 'ja',
'dayViewHeaderFormat': 'YYYY年 MMMM',
}
),
}
テンプレートの修正
extrahead
ブロックに {{ form.media }}
を追加します。
これでカレンダコントロールのウィジェットに必要なCSSとJavascriptが読み込まれます。
{% block extrahead %}
{{ form.media }}
{% endblock %}
動作確認
これでフォームにカレンダアイコンのボタンが設置され、カレンダコントロールで日時が入力できるようになりました。
以下は登録画面ですが更新画面も動きは同じです。
検索画面
フィルタの修正
次の修正を加えます。 widget
の指定はモデルフォームと同じです。
bootstrap_datepicker_plus
をインポート
- 日付項目(
DateField
)の場合は django_filters.DateFilter
の widget
引数に datetimepicker.DatePickerInput
を指定
- 日時項目(
DateTimeField
)の場合は django_filters.DateTimeFilter
の widget
引数に datetimepicker.DateTimePickerInput
を指定
import bootstrap_datepicker_plus as datetimepicker
class MyModelFilter(django_filters.FilterSet):
date_field = django_filters.DateFilter(
widget=datetimepicker.DatePickerInput(
format='%Y-%m-%d',
options={
'locale': 'ja',
'dayViewHeaderFormat': 'YYYY年 MMMM',
}
),
)
datetime_field = django_filters.DateTimeFilter(
widget=datetimepicker.DateTimePickerInput(
format='%Y-%m-%d %H:%M:%S',
options={
'locale': 'ja',
'dayViewHeaderFormat': 'YYYY年 MMMM',
}
),
)
class Meta:
テンプレートの修正
登録、更新画面と同様にフォームの media
を読み込みます。
form.media
ではなく、 filter.form.media
である点に注意です。
{% block extrahead %}
{{ filter.form.media }}
{% endblock %}
動作確認
カレンダコントロールで検索条件を入力できるようになりました。
さいごに
出来上がったプロジェクトは以下のリポジトリに格納しておきました。
github.com