LoginSignup
31

More than 5 years have passed since last update.

posted at

updated at

Organization

DjangoでModelChoiceFieldのquerysetを動的に指定する

はじめに

マスタ参照してるリストボックスについて、ログインユーザーの権限に応じて選択可能なアイテムを制御したい…というケースが発生。

↓のモデルとフォームをサンプルに、調べて試してみたやり方のメモ。

models.py
class Book(models.Model):
    name = models.charField('name',max_length=255)
forms.py
class SampleForm(forms.Form):
    book = forms.ModelChoiceField(label='book',queryset=Book.objects.all())

フォーム生成後にquerysetを上書きする

たぶん、一番シンプルな方法。

form = SampleForm()
form.fields['book'].queryset = Book.objects.filter(...)

フォームの__init__でquerysetを引数に受け取って上書きする

forms.py
class SampleForm(forms.Form):
    book = forms.ModelChoiceField(label='book',queryset=Book.objects.none())
    def __init__(self, queryset=None, *args, **kwargs):
        super(SampleForm,self).__init__(*args,**kwargs)
        if queryset :
            self.fields['book'].queryset = queryset
form = SampleForm(queryset=Book.objects.filter(...))

super.__init__をコールしないと、self.fieldsが生成されないので注意。

デフォルトのquerysetどうするかは、フォームの役割次第。

querysetそのものを受け取らず、フィルタの条件に使うデータだけ受け取っておいて、querysetの生成はフォーム側で行う…という実装でも可。

forms.py
class SampleForm(forms.Form):
    book = forms.ModelChoiceField(label='book',queryset=Book.objects.none())
    def __init__(self, permission=None, *args, **kwargs):
        super(SampleForm,self).__init__(*args,**kwargs)
        self.fields['book'].queryset = self.get_book_list(permission)

    def get_book_list(self, permission=None):
        if not permission :
            return Book.objects.none()

        if permission == AAA :
            query = Q(...)
        elif permission == BBB :
            query = Q(...)
        # 必要なだけ処理書く...

        return Book.objects.filter(query)

本質的にやってることは同じ。
要は、Form.__init__の処理が終わってから、Form.fields['field_name'].querysetを上書きすればいい。

余談

Form.fieldsをいじれば、他のフィールドのいろんな属性をフォーム生成後に変更することができる。
フィールドそのものを追加・削除することもできるかも?

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
What you can do with signing up
31