Djangoでadminにカスタムフィルタ追加
最近Pythonの勉強を始めてDjangoを弄っているのですが なかなか日本語の記事も少なく困ることも多いので 今後のPythonの発展のためにもコツコツ記事を作っていこうと思います。 Pythonのチュートリアルを一通り終えて、少しカスタマイズしたくなった方 adminのfilterをカスタマイズしたい方向けになります。
こちらも参考に。
では始めて行きましょう。 バージョン Python:3.9.4 Django:3.2
Djangoのadmin画面
チュートリアルを完了すると、このような画面が表示されるようになっていると思います。 少しだけカスタマイズしておりますが、本編とは関係ない部分ですので割愛します。 このフィルター部分ですが、検索条件を増やしてみたいですね。 できれば独自の検索条件にしたい。
filterカスタマイズ
修正前のソース
from django.contrib import admin from .models import Choice, Question from django.utils.translation import gettext_lazy as _ class ChoiceInline(admin.TabularInline): model = Choice extra = 30 class QuestionAdmin(admin.ModelAdmin): fieldsets = [ (None, {'fields': ['question_text']}), ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}), ] inlines = [ChoiceInline] list_display = ('question_text', 'pub_date', 'was_published_recently','decorate_text') list_filter = ['pub_date'] search_fields = ['question_text'] admin.site.register(Question, QuestionAdmin)
list_filter部分に「pub_date」が指定されているため、現在はpub_dateに対する検索条件のみ設定されています。
修正後のソース
from django.contrib import admin from .models import Choice, Question from django.utils.translation import gettext_lazy as _ """2.カスタムフィルターの宣言""" class QuestionTextFilter(admin.SimpleListFilter): title = _('テスト用のフィルター') parameter_name = 'question_text' def lookups(self, request, model_admin): return ( ('test', _('in test')), ('not test', _('in not test')), ) def queryset(self, request, queryset): """ Returns the filtered queryset based on the value provided in the query string and retrievable via `self.value()`. """ if self.value() == 'test': return queryset.filter(question_text__icontains = 'test') if self.value() == 'not test': return queryset.exclude(question_text__icontains = 'test') class ChoiceInline(admin.TabularInline): model = Choice extra = 30 class QuestionAdmin(admin.ModelAdmin): fieldsets = [ (None, {'fields': ['question_text']}), ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}), ] inlines = [ChoiceInline] list_display = ('question_text', 'pub_date', 'was_published_recently','decorate_text') """1.list_filter にカスタムフィルターを追加""" list_filter = [QuestionTextFilter,'pub_date'] search_fields = ['question_text'] admin.site.register(Question, QuestionAdmin)
修正点
1.list_filter にカスタムフィルターを追加
フィルターを追加するためには、「list_filter」に宣言を追加します。 修正前のソースで「pub_date」のみが入っていた部分ですね。 ここに追加するカスタムフィルターのclassを指定します。
2.カスタムフィルターの宣言
宣言したカスタムフィルターの中身です。 下記2つの定義が必要になります。サンプルソースのlookupsに記載している「test」「not test」の文字列は querysetの条件分岐に利用しています。 lookups:検索ラベルの設定 queryset:検索条件 修正後の画面はこのようになります。
ハマったエラー集
The value of 'list_filter[0]' refers to 'QuestionTextFilter', which does not refer to a Field.
ググってもなかなか解決策がわかりませんでしたが、単純なことでした。 下記のような書き方をするとエラーになります。
list_filter = ['QuestionTextFilter','pub_date']
原因は
フィルタの宣言でシングルクオートで囲ってしまっていること
シングルクオートで囲ってしまっているために、フィールド名と判断されますが 『そんなフィールドないよ』と怒られます。 カスタムフィルタはclassの宣言なのでシングルクオートなしで記載しましょう。
too many values to unpack (expected 2)
どこかのサイトからコピペして作ったらエラーになった部分 lookupsの記述方法が間違っていました。
def lookups(self, request, model_admin): return ( 'test', 'not test' )
こちらが正しいソースです。
def lookups(self, request, model_admin): return ( ('test', _('in test')), ('not test', _('in not test')), )
returnの書き方が違いますね。引数の数が違っているためにエラーとなったようです。 Pythonはルールを覚えるまでに苦労はしますが、書いていて思うのは やはり記述量の少なさ、スマートさですね。 使い方を覚えれば、開発速度が向上しそうです。