やりたいこと:Django の ModelForm を作るときに、プルダウンメニューの選択肢(select option)をデータベースのテーブルから参照したい。
結論から言いますと Foreign Key を使って別のテーブルのユニークキーをフォームの選択肢として利用します。
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>