(最終更新月:2021年10月)
前回の記事では、作成日を保存するため「DateField」の追加、カレンダー表示をするための「datepicker」の設定方法について解説しました
当記事では、「public」「date」フィールドによるフィルター機能を追加していきます
「django-filter」モジュールを使ったフィルターの作成を通じて、
- フィルターフィールドにBootstrapクラスを適用する方法
- 動的にフィルターのchoicesを操作する方法
について解説していきます
django-filterの導入やファイル構成についての細かい説明は省きますので、そもそも「django-filterを使ったことがない!」という方はまず別記事↓をご覧いただいてから先へお進みください

おススメ記事!興味ある方はこちらもどうぞ!
フィルターの完成像
作成したフィルターはこんなものになりました
ページ全体のレイアウトなどは後日整えていきますので、今回は「フィルターってどんなものか?」をご理解頂ければと思います
【公開/未公開によるフィルター】

「public」に保存された値により、「公開済み」「ドラフトのみ」を判断し、リストによる表示を変更していきます
【作成年月フィルター】

保存されている日報データの作成年月を抽出し、選択肢を作成します
対象の年月を選択すると、選択した月に作成された日報だけがリスト表示されます
こちらは保存されているデータにより動的に作成する必要がありますので、詳細は次章以降で解説します
早速始めていきましょう!
まずは「django-filter」をインストールし、設定を済ませてください
「やり方がわからない!」という方はこちらの記事を参考にしてください!
【コード】フィルターを表示するまで
filter.pyの作成まで終わりましたか?
作成したfilters.pyのコードは最終的に下記の通りです
nippo > filters.py
import django_filters
from nippo.models import NippoModel
from django.forms.widgets import Select
public_choices = ((0, "全て"), (1, "公開済のみ"), (2, "ドラフトのみ"))
class NippoModelFilter(django_filters.FilterSet):
public = django_filters.TypedChoiceFilter(
choices=public_choices,
method="public_chosen",
label="",
widget=Select(attrs={
"class":"form-select"
}))
date = django_filters.TypedChoiceFilter(
method="timestamp_checker",
label="作成月",
widget=Select(attrs={
"class":"form-select"
}))
class Meta:
model = NippoModel
fields = ["date", "public"]
def __init__(self, *args, **kwargs):
qs = kwargs["queryset"]
choice_option = [(obj.date.year, obj.date.month) for obj in qs]
choice_option = list(set(choice_option))
choice_option.sort(reverse=True)
DATE_OPTIONS = [
((year, month), f"{year}年{month}月") for year, month in choice_option
]
DATE_OPTIONS.insert(0, (None, "---"))
self.base_filters["date"].extra["choices"] = DATE_OPTIONS
super().__init__(*args, **kwargs)
def timestamp_checker(self, queryset, name, value):
if value is not None:
year, month = eval(value)
return queryset.filter(date__year=year).filter(date__month=month)
else:
return queryset
def public_chosen(self, queryset, name, value):
qs = queryset
if value == "1":
qs = qs.filter(public=True)
elif value == "2":
qs = qs.filter(public=False)
return qs
nippo > views.py
from .filters import NippoModelFilter
class NippoListView(ListView):
...
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx["filter"] = NippoModelFilter(self.request.GET, queryset=self.get_queryset())
return ctx
get_context_dataメソッドで「filter」を渡しています
ctx["filter"] = NippoModelFilter(self.request.GET, queryset=self.get_queryset())
nippo > templates > nippo > nippo-list.html
フィルターは下記のコードになります
<form method="GET">
<div class="my-3">
{{ filter.form.date.label }} {{ filter.form.date}}
</div>
<div class="my-3">
{{ filter.form.public }}
</div>
<button type="submit" class="btn btn-primary form-control">ソート</button>
</form>
フィルターを一時的にブロックタグ直下に入れておきましょう
{% extends "base.html" %}
{% block content %}
<div class="container">
<form method="GET">
<div class="my-3">
{{ filter.form.date.label }} {{ filter.form.date}}
</div>
<div class="my-3">
{{ filter.form.public }}
</div>
<button type="submit" class="btn btn-primary form-control">ソート</button>
</form>
....
ひとまずこれでフィルターは完成です
本題のBootstrapクラスの適用方法と、動的にchoicesを操作する方法について詳しく解説していきます
Bootstrapクラスを適用する方法
こちらのコードでBootstrapクラスを適用しています
public = django_filters.TypedChoiceFilter(
choices=public_choices,
method="public_chosen",
label="",
widget=Select(attrs={
"class":"form-select"
}))
date = django_filters.TypedChoiceFilter(
method="timestamp_checker",
label="作成月",
widget=Select(attrs={
"class":"form-select"
}))
この中の
widget=Select(attrs={
"class":"form-select"
})
が今回の目玉です
こちらにより、Bootstrapクラス「form-select」が適用されるようになりました
フィルターにBootstrapクラスを適用するには、
フィルター名(widget=ウィジット名(attrs={“class”: クラス名}))
となりますので、覚えておきましょう!
次章は動的にchoicesを操作する方法について解説します
関連書籍
動的にchoicesを操作する方法
Formクラスでchoicesを操作する方法と非常に似ています
「Formクラスではどうやるの?」が知りたい方はこちら↓
Formクラスでの方法と同様、__init__メソッド内でchoicesへ値を入れていきます
抜粋するとこちらになります
def __init__(self, *args, **kwargs):
qs = kwargs["queryset"]
choice_option = [(obj.date.year, obj.date.month) for obj in qs]
choice_option = list(set(choice_option))
choice_option.sort(reverse=True)
DATE_OPTIONS = [
((year, month), f"{year}年{month}月") for year, month in choice_option
]
DATE_OPTIONS.insert(0, (None, "---"))
self.base_filters["date"].extra["choices"] = DATE_OPTIONS
super().__init__(*args, **kwargs)
こちらのコード大きく分けると、
- 選択肢を作成しているパート
- 作成した選択肢をchoicesへ代入しているパート
の2つに分かれます
それぞれ説明します
保存データより選択肢を作成する
選択肢を作成している箇所はこちらです
qs = kwargs["queryset"]
choice_option = [(obj.date.year, obj.date.month) for obj in qs]
choice_option = list(set(choice_option))
choice_option.sort(reverse=True)
DATE_OPTIONS = [
((year, month), f"{year}年{month}月") for year, month in choice_option
]
DATE_OPTIONS.insert(0, (None, "---"))
ポイントとしては、
- kwargs内の「queryset」を使用していること
- 内包表記、set、sortを使い選択肢タプルを作成していること
- 選択肢の最初を「—」としていること
以上3つの点を頭に入れてコードを見ていくとわかりやすいかと思います
作成した選択肢をchoicesへ代入
作成した選択肢をchoicesに代入しているコードはこちらです
self.base_filters["date"].extra["choices"] = DATE_OPTIONS
self.base_filters[フィールド名].extra[“choices”]
でchoiceへアクセスができます
対して、Formクラスでは、
self.base_fields[フィールド名].choices
であることも合わせて覚えておきましょう!
まとめ
django-filterを使用し、フィルター機能を追加しました
「date」「public」によりフィルターする機能を通じて、
- Bootstrapクラスを適用する方法
- 動的にchoicesを操作する方法
についての解説でした
Bootstrapクラスを適用するには、フィルター内で
フィルター名(widget=ウィジット名(attrs={“class”: クラス名}))
動的にchoicesを操作するには、__init__メソッド内で、
self.base_filters[フィールド名].extra[“choices”]
となることを頭に入れておきましょう!
また、今回割愛しましたが、django-filterではもう一つ「method」を使って受け取った値を処理しています
methodについてはこちらのシリーズでは特に触れずに進みますが、別記事でご用意しますので、ご興味ある方はそちらもご覧ください
次回の記事では、ユーザー別のページを作成、コードの紹介と解説をしていきます
日報アプリを一から開発し、ウェブに公開するまではこちらにまとめています
また、公開したウェブアプリはこちら(https://nippo.itc-app.site)でご覧いただけます(公開後もアップデート中)
その他、各分野別に
を記載したページも用意しています
ご自身の目的に合わせて、お好きな箇所からご覧ください
