Fully custom validation error message with Rails

Viewed 247954

Using Rails I'm trying to get an error message like "The song field can't be empty" on save. Doing the following:

validates_presence_of :song_rep_xyz, :message => "can't be empty"

... only displays "Song Rep XYW can't be empty", which is not good because the title of the field is not user friendly. How can I change the title of the field itself ? I could change the actual name of the field in the database, but I have multiple "song" fields and I do need to have specific field names.

I don't want to hack around rails' validation process and I feel there should be a way of fixing that.

17 Answers

In your model:

validates_presence_of :address1, message: 'Put some address please' 

In your view

<% m.errors.each do |attr, msg|  %>
 <%= msg %>
<% end %>

If you do instead

<%= attr %> <%= msg %>

you get this error message with the attribute name

address1 Put some address please

if you want to get the error message for one single attribute

<%= @model.errors[:address1] %>

Try this.

class User < ActiveRecord::Base
  validate do |user|
    user.errors.add_to_base("Country can't be blank") if user.country_iso.blank?
  end
end

I found this here.

Update for Rails 3 to 6:

validate do |user|
  user.errors.add(:base, "Country can't be blank") if user.country_iso.blank?
end

Here is another way to do it. What you do is define a human_attribute_name method on the model class. The method is passed the column name as a string and returns the string to use in validation messages.

class User < ActiveRecord::Base

  HUMANIZED_ATTRIBUTES = {
    :email => "E-mail address"
  }

  def self.human_attribute_name(attr)
    HUMANIZED_ATTRIBUTES[attr.to_sym] || super
  end

end

The above code is from here

Related to the accepted answer and another answer down the list:

I'm confirming that nanamkim's fork of custom-err-msg works with Rails 5, and with the locale setup.

You just need to start the locale message with a caret and it shouldn't display the attribute name in the message.

A model defined as:

class Item < ApplicationRecord
  validates :name, presence: true
end

with the following en.yml:

en:
  activerecord:
    errors:
      models:
        item:
          attributes:
            name:
              blank: "^You can't create an item without a name."

item.errors.full_messages will display:

You can't create an item without a name

instead of the usual Name You can't create an item without a name

I recommend installing the custom_error_message gem (or as a plugin) originally written by David Easley

It lets you do stuff like:

validates_presence_of :non_friendly_field_name, :message => "^Friendly field name is blank"

A unique approach I haven't seen anyone mention!

The only way I was able to get all the customisation I wanted was to use an after_validation callback to allow me to manipulate the error message.

  1. Allow the validation message to be created as normal, you don't need to try and change it in the validation helper.

  2. create an after_validation callback that will replace that validation message in the back-end before it gets to the view.

  3. In the after_validation method you can do anything you want with the validation message, just like a normal string! You can even use dynamic values and insert them into the validation message.


#this could be any validation
validates_presence_of :song_rep_xyz, :message => "whatever you want - who cares - we will replace you later"

after_validation :replace_validation_message

def replace_validation_message
    custom_value = #any value you would like
    errors.messages[:name_of_the_attribute] = ["^This is the replacement message where 
    you can now add your own dynamic values!!! #{custom_value}"]
end

The after_validation method will have far greater scope than the built in rails validation helper, so you will be able to access the object you are validating like you are trying to do with object.file_name. Which does not work in the validation helper where you are trying to call it.

Note: we use the ^ to get rid of the attribute name at the beginning of the validation as @Rystraum pointed out referencing this gem

graywh's answer is the best if it actually locales different in displaying the field name. In the case of a dynamic field name (based on other fields to display), I would do something like this

<% object.errors.each do |attr, msg| %>
<li>
  <% case attr.to_sym %>
  <% when :song_rep_xyz %>
    <%= #display error how you want here %>
  <% else %>
    <%= object.errors.full_message(attr, msg) %>
  <% end %>
</li>
<% end %>

the full_message method on the else is what rails use inside of full_messages method, so it will give out the normal Rails errors for other cases (Rails 3.2 and up)

Related