今回のお題
今回はdjangoアプリのバリデーションについて話します。
一口にバリデーションといっても範囲がものすごく広いのですが、今回は第1回と称して「エラーメッセージの最適化」を行ってみたいと思います。
目次
- そもそもエラーメッセージをどう改良したいのか
- 改良その1〜フォームのオプションの無効化
- 改良その2〜メッセージの変更
そもそもエラーメッセージをどう改良したいのか
そもそも、エラーメッセージをどう改良したいのかを話します。
現在、書籍のレビュー投稿アプリを作成しているのですが、複数の入力項目に不備がある場合でも一つ目のエラーメッセージしか表示されない、という問題を抱えています。
以下がコードと実物です。
class Review(models.Model):
book = models.ForeignKey(
Book,
on_delete=models.CASCADE
)
name = models.CharField(
max_length=20,
verbose_name="名前",
)
body = models.TextField(verbose_name="本文", max_length=255)
def __str__(self) -> str:
return f"{self.name}: {self.body[:10]}"
class ReviewForm(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["book"].disabled = True
class Meta:
model = Review
fields = "__all__"
labels = {
"book": "書籍名",
"name": "投稿者名",
"body": "レビュー本文",
}
nameとbodyに関してはデフォルトでnull=False
とblank=False
が設定されています。
これの何が問題かというと、何も入力せずにそのまま登録ボタンを押すと一旦は投稿者名の入力を促されるのですが(写真1)、入力後に再度登録を試みると今度はレビュー本文の入力を求められます(写真2)。
エラー内容が一度に全て確認できないせいで、2度手間になっていますよね。
なのでまずはこれをなんとかしていきます。
改良その1〜フォームのオプションの無効化
いきなり結論に入るのですが、今までに出てきたエラーメッセージは(モデルの)バリデーションエラーではありません。
djangoではmodels.pyにおいてフィールドにblank=False
が設定されているとinput要素にrequired
オプションが設定される仕様になっており、それによってメッセージが表示されているというわけです。
これに関してはform
要素にnovaldate
属性を設定してあげることで解決されます(required
オプションは無効化されますが、maxlength
オプションは有効なままです)。
<form method="xxx" action="xxx" novalidate>
{{ form }}
</form>
で、その結果がこちら。
エラーメッセージが両方とも表示されていますね。
とりあえずはこれで大丈夫なのですが、メッセージが不親切な感もあるので、ここを改良していきます。
改良その2〜メッセージの変更
メッセージの編集は、models.py
ではなくforms.py
で行います。
class ReviewForm(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["book"].disabled = True
class Meta:
model = Review
fields = "__all__"
labels = {
"book": "書籍名",
"name": "投稿者名",
"body": "レビュー本文",
}
# エラーメッセージを追加
error_messages = {
"name": {
"required": "投稿者名が入力されていません",
},
"body": {
"required": "本文が入力されていません",
},
}
help_texts = {
"book": "書籍名は変更できません",
}
上記のようにerror_messages = {}
として辞書形式でメッセージを記述します。
フィールド名 > バリデーション名 > メッセージと指定するので辞書が二重になっていることに注意してください。
無事に表示できました。
まとめ
- models.pyで
null=False
(フィールドの種類によってはblank=False
)が設定されるとテンプレートにはrequiredオプション
が、モデルにはrequiredという名前のバリデーション
が設定される。 - 両者ともに有効な場合、html要素の
requiredオプション
が優先される。 -
requiredオプション
は、フォームにnovalidate属性
を付与することで無効化できる。 - バリデーションメッセージの変更は、
forms.py
から行う。
以上になります。
バリデーションには他にもcleanedメソッド
や自作のバリデーション
などいくつか方法があるので、そちらについても適宜まとめていく予定です。