Tip #14 - Custom Error Messages in Validations

Tue Apr 22 18:10:20 -0700 2008

If you use Rails, you sometimes get a situation where the custom error messages just don’t work, here is how you can fix it…

Say you have a model with a field called ‘country_iso’ which specifies the ISO value of the country the person belongs to.

Database design concerns aside (you should really have it as country_id and a separate country_iso field) you might be stuck with this situation from a legacy database (like I was).

As this is a required field, you set the following in your model:

1
2
3
class User < ActiveRecord::Base
  validates_presence_of :country_iso
end

So, you go ahead and make a form which presents the country as just a select box, because you don’t want your users to worry about if Thailand is TH or TL, but then you don’t specify a country and the error message comes back to the user in nice big letters:

Country Iso can’t be blank

Ugh!

Aside from the fact that Iso is capitalized badly, how many of YOUR users know that it stands for “International Standards Organization” and that a Country ISO is usually a two letter representation of the country? I’d wager about 3 of them have some idea :)

So you go in and change the validates line to:

1
2
3
class User < ActiveRecord::Base
  validates_presence_of :country_iso, :message => "Country can't be blank"
end

Do your test again, and now you get:

Country Iso Country can’t be blank

Nup.. that’d didn’t really fix it.

Fortunately, the fix is easy:

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

Now, reload your view and you’ll get:

Country can’t be blank

Much better!

blogLater

Mikel

  1. Ben Says:

    Hey Great Tip! Thanks for the post. Any idea how to make sure that the form field remains tagged properly with the appropriate error class so that the css rule for an error field will be applied properly? ~Ben

  2. Henrik N Says:

    Ben: You could do http://henrik.nyh.se/2007/12/full-error-messages-without-prepended-attribute-name.

    I believe another way would be to add_to_base and then add a nil error to the attribute: user.errors.add(:country_iso, nil)

    nil errors should not be listed in text but still get you the CSS error highlighting.

  3. Henrik N Says:

    Ben: You could also do http://henrik.nyh.se/2007/12/change-displayed-column-name-in-rails-validation-messages

  4. Mikel Says:

    Henrik: Nice solution! Go read Henrik’s solution, it’s quite neat!

  5. Vince Says:

    Is there a way to change the table name as well for the error message? For example, I have an STI model “class Group < Assembly” so when there is an error saving a Group, the error message says “2 errors prohibited this assembly from being saved” but I would like it to say “3 errors prohibited this group from being saved”.

  6. Luke Francl Says:

    The way I do this is by specifying full error messages for all my attributes, and then using a custom FormBuilder that displays the messages with the field (i.e., I do not use error_messages_for). With this approach, you get the benefit of having the error messages associated with the invalid attribute.

    I think the default messages are kind of like scaffolding: good for getting started, but I wouldn’t use them in a real app.

  7. Victor Moroz Says:

    I would suggest better way:

    class User < ActiveRecord::Base @@field_names = { :country_iso => ‘Country’ } cattr_reader :field_names

    ActiveRecord::Errors.class_eval do
      alias user_add add
      def add(attribute, msg)
        user_add( User.field_names[attribute.to_sym] || attribute, msg )
      end
    end

    ...

    Why? Because you can use all validates_… methods and regular error processing (how are you going to use validates_uniqueness_of?). Drawback – translation is application-wide, so User.country_iso and Session.country_iso will be translated exactly same way. Bad for existing applications where you can not choose fields.

  8. Victor Moroz Says:

    In fact I should have put this code to ApplicationController along with ActiveRecord::Errors.default_error_messages = { ...

  9. Peter Bernhauser Says:

    Great! I’d like to ask if there is a way to change default texts in header and bellow header:

    “5 errors prohibited this order from being saved There were problems with the following fields:”

    Reason – localization into different language Thanx

  10. simon Says:

    yea Peter go check out this page: http://www.softiesonrails.com/2008/4/23/better-messages-for-activerecord-validation-errors

  11. Victor Moroz Says:

    As for me I am using

    begin @User.save! rescue Exception => e flash.now[ :user_errors ] = e.message render :action => :new end

    Might not be the best thing of course, but resulting error message is concise and helps in case of other errors, not just database.

  12. Victor Moroz Says:

    Still don’t know how to paste code. You can check here - http://pastie.textmate.org/201339

    Meanwhile working example of my previous post can be found here (full translation of error messages & field names) – http://pastie.textmate.org/201340

    I happened to be a bit more complex than I expected.

  13. Gianni Says:

    How to use it in error_messages_for ???

Leave a Reply