Django の ModelForm でプルダウンをテーブルから取得する

やりたいこと:Django の ModelForm を作るときに、プルダウンメニューの選択肢(select option)をデータベースのテーブルから参照したい。

結論から言いますと Foreign Key を使って別のテーブルのユニークキーをフォームの選択肢として利用します。

  1. プルダウンにするカラムに models.ForeignKey を適用
  2. forms.py で ModelChoiceField を設定
  3. 任意のカラムの値を表出する様変更

1. プルダウンにするカラムに models.ForeignKey を適用

まず、models.py でプルダウン表示にしたい項目に models.ForeignKey を適用します。

別テーブルでユニークになっている値(つまりプルダウンの候補になる値)が入るという設定をします。

# models.py

class IgMstProduct(models.Model):
    id = models.AutoField(primary_key=True)
    product_nm = models.CharField(max_length=60, blank=True, null=True)
    brand_cd = models.ForeignKey('IgMstBrand',on_delete=models.SET_NULL, null=True, db_column='brand_cd')
    product_url = models.TextField(blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'ig_mst_product'

注意点

上記の設定時、「db_column」で DB 側で実際のカラム名を指定しないと、フォーム Submit 時に「1054, "Unknown column '〜_id' in 'field list'"」というエラーが返ってくると思います。

詳しくは調べていませんが、どうやら明示的に指定しないと Django が自動的に「〜_id」というカラム名で処理をしようとするらしいです。

2. forms.py で ModelChoiceField を設定

forms.py で、下記の様に対象の項目に対して forms.ModelChoiceField(queryset=モデル名.objects.all()) を設定してみると、それっぽいプルダウンが表示されます。

# forms.py

class IgProductForm(forms.ModelForm):
    brand_cd = forms.ModelChoiceField(queryset=IgMstBrand.objects.all())

    class Meta:
        model = IgMstProduct
        fields = ('product_nm', 'product_category', 'brand_cd', 'product_url',)

が、このままだとプルダウンの表示名が下記の様に「モデル名 object (1)」とかになるので変更します。

<!-- Chrome で「検証」した例 -->

<select name="brand_cd" required id="id_brand_cd">
  <option value="" selected>---------</option>
  <option value="1">モデル名 object (1)</option>
  <option value="2">モデル名 object (2)</option>
  <option value="3">モデル名 object (3)</option>
  <option value="4">モデル名 object (4)</option>
  <option value="5">モデル名 object (5)</option>
  <option value="6">モデル名 object (6)</option>
</select>

3. 任意のカラムの値を表出する様変更

同じく forms.py で ModelChoiceField を継承する別クラスを作成し、そこで label_from_instance 関数をオーバーライドし、return obj.表示したいカラム名 とすれば表示したいカラム名が表示名が変わります。

# forms.py

class CustomModelChoiceField(forms.ModelChoiceField):
    def label_from_instance(self, obj): # label_from_instance 関数をオーバーライド
         return obj.brand_nm # 表示したいカラム名を return


class IgProductForm(forms.ModelForm):
    brand_cd = CustomModelChoiceField(queryset=IgMstBrand.objects.all()) # 上記のクラスを参照する様変更

    class Meta:
        model = IgMstProduct
        fields = ('product_nm', 'product_category', 'brand_cd', 'product_url',)
<select name="brand_cd" required id="id_brand_cd">
  <option value="" selected>---------</option>
  <option value="1">Nikon</option>
  <option value="2">Canon</option>
  <option value="3">Sony</option>
  <option value="4">Fujifilm</option>
  <option value="5">Leica</option>
  <option value="6">Olympus</option>
</select>

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です