How to Customize Rails Validation Errors to Remove Leading Attribute Column Names
Rails validations is an elegant way to verify the model state before it's saved into the database. Often, you want to provide a custom, user-friendly error message to the user. In this post, we'll learn how to accomplish this with custom validation methods.
Let's consider the following model in Rails:
class Post < ApplicationRecord
validates :title, presence: true
end
When Rails runs the validations while creating or updating the post record, if the title attribute is missing, Rails will add the following error message to the model: "Title can't be blank".
Specifically, Rails inserts the column name (attribute) at the beginning of the error message.
You can customize this error by passing the message option to the validation:
validates :title, presence: { message: "must be provided" }This results in the following error message: "Title must be provided".
What if you don't want the column name "Title" at the start of the error message? This might be the case when you want fully custom error message, like "You must provide a valid title".
💡 Solution: To create a custom error message without the leading attribute column name, create a custom validation and add your message to the errors collection on the model's base attribute.
A custom validation method verifies the model state, and if it's invalid, the method adds the error message to the errors collection on the model.
class Post < ApplicationRecord
validate :title_must_be_present
def title_must_be_present
errors.add(:base, "You must provide a valid title") unless title.present?
end
end
The key difference between adding error to :base instead of :title is that it relates to the model's state as a whole, instead of the specific attribute.
Multiple Custom Validations
Using custom validation methods also let you perform more involved validations on the model. For example, imagine you want to ensure that the title is present and is between 5 to 20 characters long. However, you want to show the length error only when they have provided the title.
Consider this validation:
class Post < ApplicationRecord
validates :title, presence: true, length: { in: 5..20 }
end
When the title is missing, you will get the following error message: "Title can't be blank, Title is too short (minimum is 6 characters)".
Although it's technically correct, the length error is irrelevant when the title is missing entirely. In such cases, you can write a custom validation method as follows:
class Post < ApplicationRecord
validate :title_must_be_valid
private
def title_must_be_valid
errors.add(:base, "You must provide a title") and return unless title.present?
# title is present but invalid
errors.add(:base, "Title length must be within 5-20 characters") unless title.length.between?(5, 20)
end
end
Now, if the title is missing, we'll only get the error "You must provide a title" and when the title is too short or too long, we'll get the error "Title length must be within 5-20 characters".
Very cool.
Update: One of the subscribers of this blog, Goulven Champenois, provided his feedback to this post and showed a better way to accomplish this. I haven't yet had a chance to play with his solution, so copy + pasting his message below, but I will update the post after I try this solution and see how it solves the problem.
The trick you provide to removetitlefrom the error message works, but at the expense of being able to indicate which field must be corrected in the form.
This comes particularly handy to highlight the field, display the error message next to it, or whatever you might want to do. Doing this is therefore bad for UX, and bad for accessibility.
A better solution is to override the way error messages are being generated. This can be done for every error message, or just those for a given model, or even just for one of the model’s attributes.
To do that, you first need to setconfig.active_model.i18n_customize_full_messageto true in an initializer, then open your locale files and redefine eitheren.errors.format,en.activerecord.errors.models.[model_name].format, oren.activerecord.errors.models.[model_name].attributes.[attribute].format.
This is available since Rails 6, and explained in this post.
Thanks, Goulven!
Sign up for my newsletter
Let's learn to become better developers.