Djangoのフォームで関連モデルにフィルターをかける方法です。
この記事はDjangoのフォームクラスを使ってモデルの作成編集画面を作ったけど、関連テーブルの選択項目に余計な選択肢(表示したくない選択肢)が表示されてしまい、どうやって解決すればいいかわからない(>_<)!!という方を対象にしています。
最後まで読むことでフォームの選択項目にフィルターをかけて表示することができるようになります。
それでは順番に読み進めていきましょう!
[Django] モデル設定
次の3つのモデルを利用します。
User、Book、Bookshelf
今回の例でやりたいことは「ユーザー」が持っている「本」を“本のフォーム”から「本棚」に紐づけることです。
class User(AbstractUser):
email = models.EmailField(('メールアドレス') )
class Bookshelf(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
name = models.CharField(("本棚名"),max_length=150)
class Book(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
bookshelf = models.ForeignKey(Bookshelf,on_delete=models.CASCADE)
title = models.CharField(("タイトル"),max_length=150)
モデルは最小構成でこんな感じでいきます。特に中身はありません。
気にすべきところは Book-User Book-Bookshelf が関連モデルになっていることです。
Formクラスの設定
まずはBookのフォームクラスを作成します。
class BookEditForm(forms.ModelForm):
class Meta:
model = Book
fields = ['title',"bookshelf"]
ここまでは普通ですね。 fields に user を追加していないのはログイン後の画面で編集画面を開いている場合はログインユーザーが自動的にuserとして設定されるのでわざわざフォームでuserを選択しなくていいからです。
[Django]フォームクラスでモデルにフィルターをかける方法
ここからが本題です。モデルに対してフィルターをかけるには下記のように指定します。
self.fields['bookshelf'].queryset = Bookshelf.objects.filter(user_id=id)
fieldsでフィルターをかけたいモデルを指定します。そして、.queryset
とすることでその検索方法の変更ができます。
ユーザーの本棚だけを表示させて選択できるようにしたいので下記のようにuser_id=id
でIDを指定します。
Bookshelf.objects.filter(user_id=id)
この辺りはご自身がフィルターしたい内容に合わせて書き換えてください。
user_id=id
右辺のidはkwargs.get(‘instance’)をすることで現在のBookのインスタンスを取得できます。
id = kwargs.get("instance").user.id
取得したBookインスタンスはBookモデルのpk,user,title,bookshelf
を取り出すことができます。
ここではユーザーIDを取得したかったので.user.id
を使用しています。
完成したBookEditFormクラスの全体像はこの様になります。
class BookEditForm(forms.ModelForm):
class Meta:
model = Book
fields = ['title',"bookshelf"]
def __init__( self, *args, **kwargs):
super(BookEditForm,self).__init__(*args, **kwargs)
id = kwargs.get("instance").user.id
self.fields['bookshelf'].queryset = Bookshelf.objects.filter(user_id=id)
まとめ
- フォームにフィルターをかける方法は `self.fields[‘フィールド名’].queryset`を書き換える。
- フォーム内でユーザーを指定するには `kwargs.get(“instance”)`でインスタンスを取得した後、user.idから取得。
Djangoを利用するときの参考になれば幸いです^^
別のアプローチもあるので上の方法がうまくいかない方はこちらの方法もお試しください。
コメントを残す