BLOGサブスレッドの日常
2016.05.31
DjangoのFormを活用する (1) ModelChoiceField
mzsm
株式会社サブスレッドのみずしま a.k.a mzsm(@mzsm_j)です。こんにちは。
DjangoのForm、ちゃんと使えばかなり優秀でいろいろと楽できるですけど、できることが多すぎて使いこなせてないケースもあるかと思います。
今回はその中でもModelChoiceFieldについて。
「DBに格納されている値の中からどれか選ばせたい?そんなときModelChoiceFieldが便利なんですよ。」
…ということはDjango使いなら皆知っているかと思いますが、ちゃんと使いこなせてますか…?
例えばこのようなモデルとテーブルが存在しているとします。
from django.db import models
class Product(models.Model):
"""商品モデル"""
code = models.CharField('商品管理番号', max_length=30, unique=True)
name = models.CharField('商品名', max_length=100)
def __str__(self):
return self.name
id | code | name |
---|---|---|
1 | MIKAN | みかん |
2 | RINGO | りんご |
3 | BANANA | バナナ |
そして、このようなフォームを作成すると…
from django import forms
from . import models
class ProductForm(forms.Form):
product = forms.ModelChoiceField(models.Product.objects, label='商品')
amount = forms.IntegerField(min_value=1, max_value=10, label='個数')
だいたいこんな感じのHTMLが出力されます(実際にはidやら何やらが入りますがここでは省略)
<form>
<div>
<label>商品</label>
<select>
<option value="">---------</option>
<option value="1">みかん</option>
<option value="2">りんご</option>
<option value="3">バナナ</option>
</select>
</div>
<div>
<label>個数</label>
<input type="number">
</div>
</form>
それではこれをもとにいろいろいじっていきましょう。
未選択の場合の選択肢を変える
項目が未選択の場合、デフォルトでは---------
という項目が表示されますが、これを例えば選択してください
に変えたい場合はempty_label
を指定します。
from django import forms
from . import models
class ProductForm(forms.Form):
product = forms.ModelChoiceField(models.Product.objects, label='商品',
empty_label='選択してください')
amount = forms.IntegerField(min_value=1, max_value=10, label='個数')
↓出力HTML
<form>
<div>
<label>商品</label>
<select>
<option value="">選択してください</option>
<option value="1">みかん</option>
<option value="2">りんご</option>
<option value="3">バナナ</option>
</select>
</div>
<div>
<label>個数</label>
<input type="number">
</div>
</form>
valueをid以外にする
valueはデフォルトでは主キー(pk
=id
)が使用されますが、シーケンシャルなIDをユーザーにあまり見せたくないとか、業務で利用しているコードを使用したいといったような場合には、to_field_name
にフィールド名を指定することで、そのフィールドを値として指定できます。
from django import forms
from . import models
class ProductForm(forms.Form):
product = forms.ModelChoiceField(models.Product.objects, label='商品',
empty_label='選択してください', to_field_name='code')
amount = forms.IntegerField(min_value=1, max_value=10, label='個数')
↓出力HTML
<form>
<div>
<label>商品</label>
<select>
<option value="">選択してください</option>
<option value="MIKAN">みかん</option>
<option value="RINGO">りんご</option>
<option value="BANANA">バナナ</option>
</select>
</div>
<div>
<label>個数</label>
<input type="number">
</div>
</form>
セレクトボックスからの選択ではなく、値を直接指定させる
候補一覧を表示して選択させるのではなく、ユニークなコードを直接入力させるほうが便利な場合もあるでしょう。
widget
にTextInput
に変更することで、セレクトボックスではなく文字列で入力させられるようになります。
これにより、ModelChoiceFieldを簡単な検索フォームの部品として利用できます。
CharFieldを使う場合、is_valid()でフォームの妥当性を検証した上で、改めてモデルを検索してそのコードに一致するレコードがあるかどうかを調べる、という2段階で処理を行う必要がありますが、
ModelChoiceFieldを使えばis_valid()を呼び出すだけでそのコードに一致するレコードがあるか調べられるので、ちょっぴり楽です。
from django import forms
from . import models
class ProductForm(forms.Form):
product = forms.ModelChoiceField(models.Product.objects, label='商品',
empty_label='選択してください', to_field_name='code',
widget=forms.TextInput)
amount = forms.IntegerField(min_value=1, max_value=10, label='個数')
↓出力HTML
<form>
<div>
<label>商品</label>
<input type="text">
</div>
<div>
<label>個数</label>
<input type="number">
</div>
</form>
このとき、存在しないコードを入力すると、正しく選択してください。選択したものは候補にありません。
というメッセージが表示されます。
選択しているわけではないので、このエラーメッセージだと違和感があります。
error_messages
を指定して、違和感のないエラーメッセージを表示するようにするのがよいでしょう。
from django import forms
from . import models
class ProductForm(forms.Form):
product = forms.ModelChoiceField(models.Product.objects, label='商品',
empty_label='選択してください', to_field_name='code',
widget=forms.TextInput,
error_messages={'invalid_choice':'このコードの商品は存在しません'})
amount = forms.IntegerField(min_value=1, max_value=10, label='個数')
こちらからは以上です。
この記事を書いた人
mzsm