Edited at

【Rails】バリデーションチェックのエラーメッセージは1項目1つまでにしてすっきり。


Problem

Railsでは、modelのバリデーションエラーのメッセージをmodel.errors.full_messagesで扱うことができてすごく便利だなぁと思っています。

ただ、例えばあるattributeが「必須の数字で、1以上で、10以下」というvalidatesが定義されている時に、このattributeに対してnullを設定すると、full_messagesには「null」「1以上でない」「10以下でない」に対して3つもエラーメッセージが出てきてしまいます。

とりあえずブレスト」という業務効率化Webサービスを作った時に、エラーが重複して出るのはダサいなと思いちょっとハマったので残しときます。


対象となるモデル

対象のモデルは例えば以下のような設定がされているとする。


app/models/sample.rb

#--略--#

validates :number, presence: true
validates :number, numericality: {greater_than_or_equal_to: 1}
validates :number, numericality: {less_than_or_equal_to: 10}
#--略--#

1行目で必須、2行目で1以上の数字、3行目で10以下の数字の条件をそれぞれ定義しています。

実はnumericalityはデフォルトでnullを許容していないこともあり、

numberをnullに設定すると3つ全てのvalidatesに引っかかってしまう。


対象となるビュー

Railsでscaffold機能などを使うとデフォルトで以下のようなエラー表示エリアが用意されているはず。


app/views/sample/sample.html.erb

<!--略-->

<% if @sample.errors.any? %>
<ul>
<% @sample.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
<!--略-->

この時、full_messagesには上で説明した3つのvalidates分のメッセージが格納されてしまっているので、3つともエラーメッセージが出力されてしまう。


Solution


モデルに項目が一つしかないなら

full_messages_for(attribute)というメソッドを利用します。

これはattributeに対してのfull_messagesを配列で返してくれるメソッドです。

例えば、attributeにcountを指定してあげれば、

["countを入力してください", "countは1以上の値にしてください", "countは10以下の値にしてください"]

といった配列を返却してくれるのです。

これを使って


app/views/sample/sample.html.erb

<!--略-->

<% if @sample.errors.any? %>
<ul>
<li><%= @sample.errors.full_messages_for(:count).first %></li>
</ul>
<% end %>
<!--略-->

としてあげることで、配列の1番目の"countを入力してください"だけが表示されるようになります。


モデルに項目が複数あっても

おおよそモデルには1つ以上の項目が定義されていると思います。

そんな時は、errorを起こしている項目を抽出して1つずつfull_messages_forに入れていけばよさそうです。

次はerrors.messagesメソッドを使っていきます。

このメソッドは、エラーが起きた項目と項目名をのぞいたメッセージ("を入力してください"のような)をハッシュ形式で返却してくれます。

この「エラーが起きた項目」の方だけを抜き出して、full_messages_forに入れちゃえばやりたいことができます。


app/views/sample/sample.html.erb

<!--略-->

<% if @sample.errors.any? %>
<ul>
<% @sample.errors.messages.each do |attr, msgs| %>
<li><%= @sample.errors.full_messages_for(attr).first %></li>
<% end %>
</ul>
<% end %>
<!--略-->

これでモデルに複数の項目が定義されている場合でも、エラーメッセージを1項目1つまで表示されることができました。


Conclusion

1つの項目に対して何個もエラーメッセージが出てくると「しつこいなぁ」という気分になるので、こんな感じでエラーメッセージをすっきりさせるとよいと思いました!


Reference