ModelChoiceField
Djangoではクエリー結果をフォームフィールドの選択肢に使用するための ModelChoiceField が用意されている。
例えば下記の様に queryset引数にクエリー結果を渡すと選択肢の値にはMemberモデルのPKであるidが使用され、表明にはモデルの__str__メソッドの戻り値が使われる。
class Member(models.Model):
id = models.AutoField ( primary_key=True )
name = models.CharField ( max_length=32 )
email = models.EmailField( max_length=200, null=False, unique=True)
def __str__(self):
return self.name
class SearchForm(forms.Form):
email = forms.ModelChoiceField(queryset=Member.objects.all())
# このフォームをテンプレートで表示するとoptionの中身はこんなイメージ
# <option value="[ obj.id ]">[ str(obj) ]]</option>
PK以外を選択肢にする実装(良くない例)
選択肢の値にPK以外(ここではemail)を使用したいのでフォームフィールドの型をChoiceFieldに変更して choices にクエリー結果からemailとnameのリストを渡す事で実装できる。
class SearchForm(forms.Form):
email = forms.ChoiceField(
choices=Member.objects.values_list('email','name')
)
しかし、この実装では新規モデルをマイグレーションすると__django.db.utils.ProgrammingError: (1146, "Table doesn't exist")__が発生する。
これは、[フォームが初期化される際にクエリーを実行しようとするのでエラーになってしまう]
(https://stackoverflow.com/questions/45234291/django-form-using-choices-against-a-queryset-causes-error-if-part-of-initial-mak "Qiita Home")事が原因なのでマイグレーション時だけ当該フィールドをコメントアウトする方法で回避は可能だが。。。
上記リンク先ではベストプラクティスとして別の関数でクエリーを実行して関数をchoicesに渡す方法を紹介している。これはこれで選択肢を自由に書き換えられるという意味では便利。
ベストプラクティス
ModelChoiceField の to_field_name を使用する。
class SearchForm(forms.Form):
email = forms.ModelChoiceField (
queryset=Member.objects.all(),
to_field_name="email"
)
# このフォームをテンプレートで表示するとoptionの中身はこんなイメージ
# <option value="[ obj.email ]">[ str(obj) ]]</option>
こうする事でフォームをテンプレートで展開する際に option タグのvalueにはemailが使用され表示名にはモデルの__str__メソッドが使用される。