11
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

DjangoAdvent Calendar 2019

Day 3

Django管理画面で選択できる外部キーを絞る(Filterする)方法

Last updated at Posted at 2019-12-02

この記事はDjango Advent Calendar 2019 3日目の記事です。
(アドベントカレンダーは)初投稿です。

はじめに

ここ1ヶ月くらいDjangoで個人的にアプリを作ってる非エンジニアの人です。
そのアプリで外部キーを使用したところ、プルダウンを開くと100件以上表示されてしまうので、
なんとか絞れないかなと思いました。

先に結論を言えば、limit_choices_toを使うことで、外部キーで表示されるレコードをfilterすることが可能です。
どのように使えば良いか、実際にアプリを作成して試してみます。

この記事の為に作ったアプリ

説明の為に簡単なアプリを作成しました。Modelだけです。
ちなみにテーマとしては、2019年にFA権を取得した選手達がどの球団に行くかの予想を登録するアプリです。
もうだいたい決まったから実用性はないな!
https://github.com/shimayu22/fa_expects_app/

FaExpectsテーブルでは、Playersテーブルを外部キー設定し、登録されている選手達がプルダウンで選択できるようにしています。
選手データも用意しているので読み込むだけでお試しできます。
使い方はREADMEを参照してください。

こういうこと

スクリーンショット 2019-11-23 23.01.18.png

python manage.py runserverで検証しています
http://127.0.0.1:8000/admin/fa_expects/faexpects/add/
の「選手」のプルダウンを開くと、
画像のようにズラーっと表示されてしまいます。

管理画面でFilterできんのか、という話です。

limit_choices_toを利用する

DjangoドキュメントのForeignKey.limit_choices_toより、

このフィールドがModelFormまたはadmin を使用してレンダリングされるときに、このフィールドで使用可能な選択肢に制限を設定します(デフォルトでは、クエリセット内のすべてのオブジェクトを選択できます)。
辞書、Qオブジェクト、または辞書またはQオブジェクトを返す呼び出し可能オブジェクトのいずれかを使用できます。
(Google翻訳)

ということなので、limit_choices_toを使えば選択できる項目をFilterすることができます。

実際にやってみた

FaExpectsのplayer_idにlimit_choices_toを追加しました。

models.py
    player_id = models.ForeignKey(
        Players,
        on_delete=models.CASCADE,
        verbose_name="選手",
        limit_choices_to={"position":1,}
    )

今回はpositionが「1」(投手)だけ選択できるようにします。
これで、
http://127.0.0.1:8000/admin/fa_expects/faexpects/add/
の「選手」のプルダウンを開くと、

スクリーンショット 2019-11-24 22.52.48.png

見事、投手として登録した選手だけ表示されました!

検索キーワードを使用する

上記の例だと、position == 1など、イコールでしか絞れませんが、検索キーワードを使用することで、以上、以下などを用いて絞ることが可能です。

models.py
    player_id = models.ForeignKey(
        Players,
        on_delete=models.CASCADE,
        verbose_name="選手",
        limit_choices_to={"position": 1,
                          "age__lt": 33}
    )

上記はposition == 1 and age < 33(投手かつ33歳未満)という条件を付けます。
項目名age__lt(アンダーバー2つ)をつけてると「~未満」という条件が加わります。

スクリーンショット 2019-11-24 23.01.15.png

選択できる選手が少なくなりましたね!
その他のキーワードについては下記の記事がとてもよくまとまっています。

参考:Django データベース操作 についてのまとめ#検索キーワードの一覧

Qオブジェクトを利用する

これまでは辞書型で検索条件を指定していましたが、Qオブジェクトでも指定することが可能です。

models.py
from django.db.models import Q

~~~省略~~~
    player_id = models.ForeignKey(
        Players,
        on_delete=models.CASCADE,
        verbose_name="選手",
        limit_choices_to=Q(position=4) | Q(position=7),
    )

Qオブジェクトだと、このようにOR条件を指定できます。
上記では「ポジションが二塁手 or 外野手」という意味になります。

スクリーンショット 2019-11-24 23.37.01.png

見事、二塁手または外野手だけ表示することができました!

limit_choices_to用の辞書を作る

べた書きだと柔軟性がないので、関数で辞書型を作って渡してあげます。
今回はサクッと作るために、条件を指定する用のテーブルを作りました。
Models.pyRequestedConditionsクラスを追加しています。
(長いのでModels.pyで確認してください)

そして、RequestedConditionsテーブルの最新レコードからlimit_choices_to用の辞書を作成するための関数を作成しました。

Models.py
〜〜〜省略〜〜〜

def set_players_condition():
    condition =  RequestedConditions.objects.latest('pk')
    condition_dict = {}
    if condition.age > 0:
        condition_dict["age__lt"] = condition.age
    
    if condition.position > 0:
        condition_dict["position"] = condition.position
    
    if condition.dominant_hand > 0:
        condition_dict["dominant_hand"] = condition.dominant_hand

    return condition_dict

〜〜〜省略〜〜〜

まず、
http://127.0.0.1:8000/admin/fa_expects/requestedconditions/add/
より、「年齢」「ポジション」「利き手」を設定し、保存します。
(全部設定しなければ全部表示されます)

スクリーンショット 2019-11-26 23.31.31.png

登録後、
http://127.0.0.1:8000/admin/fa_expects/faexpects/add/
の選手を見てみると、条件にあった選手のみ表示されます。

スクリーンショット 2019-11-26 23.31.57.png

やったぜ。

おわりに

モデルだけでちょろっとやりたい時はこんな感じでできます。
Qオブジェクトも組み合わせればもっと柔軟にできそうですね。
色々いじって試してみてください。

おまけ

冷静に考えれば当たり前かもしれませんが、
limit_choices_to={"age__lt":RequestedConditions.objects.latest('pk').age}
みたいな感じで書いてしまうと、最初にmigrateした時に「そんなテーブルないぞ」というエラーが出ます。
そら(まだ作成されていないテーブルを参照しようとしたら)そう(エラーが出る)よ(当たり前じゃないか)。

あまり関係ない補足

  1. FA宣言した選手達ではなく、FA権を取得した選手達です(間違いがあったらゴメンね)
  2. 鈴木大地(ロ)は色々守れすぎなので、2019年一番多く守ってるっぽい(一)としました
  3. 私はちなヤクちなハムです
  4. 来シーズンもやきう楽しみですね!

参考

Django データベース操作 についてのまとめ

また、Django全般で下記記事が参考になりました。ありがとうございます!
[Python] Djangoチュートリアル - 汎用業務Webアプリを最速で作る
[Django] モデルフィールド 設定テンプレート

11
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?