【Django(日報アプリ開発)】django-filterでBootstrapクラスを適用、動的にchoicesを操作する方法を解説!

Django

(最終更新月:2021年10月)

前回の記事では、作成日を保存するため「DateField」の追加、カレンダー表示をするための「datepicker」の設定方法について解説しました

当記事では、「public」「date」フィールドによるフィルター機能を追加していきます

「django-filter」モジュールを使ったフィルターの作成を通じて、

  1. フィルターフィールドにBootstrapクラスを適用する方法
  2. 動的にフィルターの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, "---"))

ポイントとしては、

  1. kwargs内の「queryset」を使用していること
  2. 内包表記、set、sortを使い選択肢タプルを作成していること
  3. 選択肢の最初を「—」としていること

以上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についてはこちらのシリーズでは特に触れずに進みますが、別記事でご用意しますので、ご興味ある方はそちらもご覧ください

次回の記事では、ユーザー別のページを作成、コードの紹介と解説をしていきます

タイトルとURLをコピーしました