この記事は、Django Advent Calendar 2022 8日目の記事です。
昨日は、おっと! 枠がまだ空いているようですね。
Djangoに興味があるそこのあなた! よろしければ時を戻した投稿をしてみませんか?🎸
はじめに
楽してこーぜ!、百沢1じゃなくてnikkieです。
Django Advent Calendarに一ネタ投稿です!
私はDjangoを実務でバリバリ使っているわけではありません。
趣味開発のような感じで、PyCon JPのスタッフ活動でごく小規模なアプリを開発し、他のスタッフにも使っていただきました。
その中で学んだDjangoフォームについて共有していきます。
目次
- はじめに
- 目次
- 背景:PyCon JPスタッフ活動で作ったDjangoアプリ
- レビュアーがプロポーザルを検索できるフォーム
- テンプレートによる実装(2020年)
- Djangoフォームによる実装(2021年)
- 学び:Djangoフォームを使おう!
- 終わりに
- P.S. Djangoフォーム周りの積ん読
背景:PyCon JPスタッフ活動で作ったDjangoアプリ
PyCon JPスタッフ活動の中で、プロポーザル(「私はこういうトークをしたい」という文書)のレビューに使うアプリをDjangoで自作しました2。
2020年、2021年(座長の年!3)と2回作っています。
2回目(2021年)は、1回目(2020年)でイマイチだった点のリベンジが個人的なテーマでした4。
そのうちの1つが、レビュアーがプロポーザルを検索できるフォームです5。
レビュアーがプロポーザルを検索できるフォーム
プロポーザルレビューアプリでは、2020年のレビュー中の声を元に、以下の項目で検索できる機能を追加しました(2021年でも継続して実装)。
- 時間枠(2020年のみ)
- 聴衆のPythonのレベル(Beginner/Intermediate/Advanced)(2021年のみ)
- 発表の言語
- セッションのカテゴリ6
- セッションタイトルに含まれる語(あいまい検索)
- 自身がすでにレビューしたプロポーザルを除いて検索
このフォームはプロポーザルの一覧画面にあります。
モデルには結びつかないフォームです(ModelForm
は考えませんでした)。
検索して遷移した一覧画面ではフォームの入力値(検索条件)を保持して表示するようにもしています(テンプレートタグを作って対応)。
2020年(Django 3.0系で開発)はテンプレートで実装しました。
ビューからテンプレートにデータを渡し、テンプレートタグをゴリゴリ使って検索条件を保持しつつフォームを描画します。
実装を終えて振り返ると、「テンプレートをゴリゴリ書くのが正解だったのだろうか? もっとうまくやる方法があったのでは?」と疑問に思えてきました。
そこで2021年の機会(Django 3.2系で開発)ではDjangoフォームを試しています。
Djangoフォームで実装する中では、Pythonをゴリゴリ書きました。
2020年よりもPython(慣れた言語)に触る時間が長く、その中で発見も多くあり、個人的には開発していて楽しかったです。
テンプレートによる実装(2020年)
テンプレートは以下のようになっています(一部に絞っています)。
ビューから変数を渡します。
@login_required def list_proposals(request): # 省略 # selectタグの表示とvalueを別にするために辞書を作っています(共通の場合はcontextにあるように.valuesだけ渡します) speaking_language_map = { label: value for value, label in Proposal.SpeakingLanguage.choices } context = { "page_obj": page_obj, "proposals_count": paginator.count, # 以下はプロポーザル検索フォームの表示に使う "session_formats": Proposal.SessionFormat.values, "speaking_languages": speaking_language_map, "categories": Proposal.SessionCategory.values, } return render(request, "review/list_proposals.html", context)
Proposal
モデルのTextChoices
7を使って、フォームの<select>
タグに必要な情報(=すべての選択肢)を送り込みます。
表示上、検索条件を保持するために、組み込みタグのwith
を使いました。
複雑な表現の変数の値をキャッシュし、簡単な名前で参照できるようにします。
ユーザが選んでいる選択肢をテンプレートで知りたかったのです。
実装詳細です。
request.GET
には検索条件を表すクエリパラメタがあります。
自作のテンプレートタグparse_search_parameter
でクエリパラメタから値を取得します。
そして、クエリパラメタに含まれる値(=ユーザが選んでいる値)をwith
タグを使って保持します。
フォームの描画ではwith
タグを使って、保持した値と一致する<select>
タグにだけselected
が付くようにします。
以上でユーザが選んでいる選択肢がフォームに表示されます。
Djangoフォームによる実装(2021年)
2021年の実装は以下のリポジトリで公開しています(先ほどの画像も2021年版です)。
forms.py
にプロポーザル検索用のフォームを定義しました。
テンプレートでDjangoのタグ(組み込み・自作両方)とHTMLを駆使した経験からは、かなり楽になりました。
ビューでの扱いはこんな感じです8。
@login_required def list_proposals(request): # 省略 form = ProposalSearchForm(request.GET) context = { "page_obj": page_obj, "proposals_count": paginator.count, # 以下はプロポーザル検索フォームの表示に使う "form": form, } return render(request, "review/list_proposals.html", context)
選択肢を表示するために送るデータがなくなり、かなりスッキリしました✨
フォームを構成するデータに注目すると違いは歴然です。
Djangoフォームを使う実装ではフォームを定義し、テンプレートではrequest.GET
を渡してインスタンス化するだけです。
こんなに単純な実装になるなんて!
Djangoフォームを使う実装で対処しきれなかったのは、チェックマーク✅の表示です。
非表示にしたいと思ったのですが、2021年当時は調べ切るには時間が足りず、「影響は表示レベルなので実装者のこだわりの問題」と対応優先度を下げ、そのままとしました。
宿題としておきます〜
学び:Djangoフォームを使おう!
モデルに結びつかないフォームにもDjangoフォームを使うのがオススメです。
たしかにテンプレートで頑張れるかもしれません。
ただ、ユーザがフォームに入力したデータを表示上保持する実装は、テンプレートで頑張るよりDjangoフォームを使ったほうが簡潔になります。
2020年のnikkie氏の言ですが、DjangoではModelForm
しか知らず9「Form
はなんかよく分からないから、テンプレートでやっちゃった」とのことです。
やっちゃった後に違和感を覚えてDjangoフォームに対峙したわけですが、回り道をした今なら言えます、「Djangoフォームを使おう!」
終わりに
このアウトプットが2020年の私と同じく「モデルに結びつかない検索フォーム、知らないForm
を使うか、知っている知識でできそうなテンプレートでやっちゃうか」迷っている方の意思決定の一助となったらいいなと思います(時を戻そう!)。
オススメはDjangoフォームです。
コード量が段違いで少ないですし、Pythonを書くだけで実現できます!
楽してこーぜ🎸
明日のDjango Advent Calendar 2022 9日目は、この記事を読んでくださった あなた かもしれません。
私は「アドベントカレンダーだからと気張らずに淡々と自分のペースで小ネタをアウトプット」という立場です。
もしよければ小ネタだったり、「自分の知ってるこれ、実は小ネタじゃないのかも」だったりを共有していただけるととても嬉しいです(絶対読みますからね!)
P.S. Djangoフォーム周りの積ん読
より深く理解するのに役立ちそうな積ん読(崩し中)リストを共有します。
『実践Django』
第5章がDjangoフォームです。
ModelForm
が詳しいです。
『実践Django』はDBやセキュリティ(例:CSRF)など裏の仕組みが詳しく分かるのがありがたいですね。
『現場で使えるDjangoの教科書』
第8章がフォームです。
Form
もModelForm
も紹介されていますね。
Django Congress JP 2021 「理解して使いこなすDjangoのForm機能」
きゅうたつさんによるフォームのトーク、非常に丁寧でした!
復習するとよさそうですね(https://djangocongress.jp/2021.html からアーカイブも見られます)
Django Congress JP 2022 「Djangoで管理する全文検索エンジン」
あいまい検索で思い出した「全文検索は素振りしたいな〜」という気持ちのままに積んでおきます。
Elasticsearch触ってみたい〜
- ハイキュー!!より。アニメでは4期(TO THE TOP)。稲荷崎戦めっちゃ熱かった🏐 全部見てから[禁則事項]に打ち震えました↩
- このアプリはHerokuにデプロイしていましたが、無料プラン終了に伴い、先日Web APIを生やしてデータをエクスポートし、お片付けしました。 プロポーザルやレビューがどんなデータかも簡単に書いていますので、よろしければ合わせてご参照ください↩
- ちょっとしたWebアプリなら自作できる座長です😘。今後は自動化やアプリケーション開発などコードでコミュニティにコントリビュートしていきたいな〜(お話ウェルカム)↩
- 私のアツいスタッフ活動!技術で(も)支えたPyCon JP 2021 #pyconjp - nikkie-ftnextの日記 にて、「プロポーザルレビューアプリ」の技術的なアップデートを共有しています(ここにフォームはありませんね)↩
- テストデータを入れたバージョンを静的化しています。画像も静的化したバージョンです(「絞り込む」ボタンを無効化) ↩
- Trackとも呼ぶかもしれません↩
- https://docs.djangoproject.com/ja/3.2/ref/models/fields/#enumeration-types↩
- https://github.com/pyconjp/pycon.jp.2021.review/blob/352ec3f2ac5d9473ed723882597287f23f4e79ab/reviewsite/review/views.py#L24-L50↩
-
Django Girls Tutorial育ちなので、
ModelForm
は手に馴染みまくっていました。 ref: Djangoフォーム · HonKit↩