1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

djangoのバリデーションについてまとめる(第1回〜エラーメッセージの最適化)

Last updated at Posted at 2021-11-04

今回のお題

今回はdjangoアプリのバリデーションについて話します。

一口にバリデーションといっても範囲がものすごく広いのですが、今回は第1回と称して「エラーメッセージの最適化」を行ってみたいと思います。

目次

  • そもそもエラーメッセージをどう改良したいのか
  • 改良その1〜フォームのオプションの無効化
  • 改良その2〜メッセージの変更

そもそもエラーメッセージをどう改良したいのか

そもそも、エラーメッセージをどう改良したいのかを話します。

現在、書籍のレビュー投稿アプリを作成しているのですが、複数の入力項目に不備がある場合でも一つ目のエラーメッセージしか表示されない、という問題を抱えています。

以下がコードと実物です。

models.py
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]}"
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": "レビュー本文",
    }

nameとbodyに関してはデフォルトでnull=Falseblank=Falseが設定されています。

実物
Image from Gyazo

これの何が問題かというと、何も入力せずにそのまま登録ボタンを押すと一旦は投稿者名の入力を促されるのですが(写真1)、入力後に再度登録を試みると今度はレビュー本文の入力を求められます(写真2)。

写真1
Image from Gyazo

写真2
Image from Gyazo

エラー内容が一度に全て確認できないせいで、2度手間になっていますよね。

なのでまずはこれをなんとかしていきます。

改良その1〜フォームのオプションの無効化

いきなり結論に入るのですが、今までに出てきたエラーメッセージは(モデルの)バリデーションエラーではありません。

djangoではmodels.pyにおいてフィールドにblank=Falseが設定されているとinput要素にrequiredオプションが設定される仕様になっており、それによってメッセージが表示されているというわけです。

これに関してはform要素にnovaldate属性を設定してあげることで解決されます(requiredオプションは無効化されますが、maxlengthオプションは有効なままです)。

<form method="xxx" action="xxx" novalidate>
  {{ form }}
</form>

で、その結果がこちら。

Image from Gyazo

エラーメッセージが両方とも表示されていますね。

とりあえずはこれで大丈夫なのですが、メッセージが不親切な感もあるので、ここを改良していきます。

改良その2〜メッセージの変更

メッセージの編集は、models.pyではなくforms.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 = {}として辞書形式でメッセージを記述します。

フィールド名 > バリデーション名 > メッセージと指定するので辞書が二重になっていることに注意してください。

結果
Image from Gyazo

無事に表示できました。

まとめ

  • models.pyでnull=False(フィールドの種類によってはblank=False)が設定されるとテンプレートにはrequiredオプションが、モデルにはrequiredという名前のバリデーションが設定される。
  • 両者ともに有効な場合、html要素のrequiredオプションが優先される。
  • requiredオプションは、フォームにnovalidate属性を付与することで無効化できる。
  • バリデーションメッセージの変更は、forms.pyから行う。

以上になります。
バリデーションには他にもcleanedメソッド自作のバリデーションなどいくつか方法があるので、そちらについても適宜まとめていく予定です。

1
2
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
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?