LoginSignup
0
0

More than 1 year has passed since last update.

PK以外のフィールドを選択肢に選択フィールドを作成する場合 ChoiceFieldではなくModelChoiceFieldのto_field_nameを使用する。

Posted at

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")が発生する。
これは、フォームが初期化される際にクエリーを実行しようとするのでエラーになってしまう事が原因なのでマイグレーション時だけ当該フィールドをコメントアウトする方法で回避は可能だが。。。

上記リンク先ではベストプラクティスとして別の関数でクエリーを実行して関数をchoicesに渡す方法を紹介している。これはこれで選択肢を自由に書き換えられるという意味では便利。

ベストプラクティス

ModelChoiceFieldto_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__メソッドが使用される。

0
0
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
0
0