Creating Custom Validations in Rails
by Ícaro Falcão
Validations are typically run before save or update commands, this means it happens before any object is sent to the database. If any validations fail, the object will be marked as invalid and Active Record will not perform the INSERT or UPDATE operation. This avoids storing an invalid object in the database. You can choose to have specific validations run when an object is created, saved, or updated.
Let's say we have a model Lecture with a column for the start_time and another one to end_time and we don't t want to save in our database a lecture starting after it has already finished, that should be impossible right?
In this case, we can use the
validate method, it allow us to create a custom method and register it to run before the creation and update by passing the method name with a symbol, let's see how it should look:
class Lecture < ApplicationRecord validate :end_time_cannot_be_earlier_than_start_time def end_time_cannot_be_earlier_than_start_time end end
Good! Now we need to implement the condition to raise an error, for this task we'll use a method provided by rails to add an error message to the object. To understand it, an Active Record object contains an array property called
errors and we can add an error to this property. In our validation we only need to make a simple condition to not allow a lecture end before it has even started:
if self.end_time <= self.start_time errors.add(:lecture, "can't start after it's already finished") end
This will add an error message like Lecture can't start after it's already finished. Let's see how our model should look like now:
class Lecture < ApplicationRecord validate :end_time_cannot_be_earlier_than_start_time def end_time_cannot_be_earlier_than_start_time if self.end_time <= self.start_time errors.add(:lecture, "can't start after it's already finished") end end end
In case you don't want to set an error to a specific attribute, there is a method to add error messages related to the object itself and not the attribute value.
errors[:base] << "This is invalid because..."
You can pass more than one method for each class method and the respective validations will be run in the same order as they were registered.
validate :end_time_cannot_be_earlier_than_start_time, :some_other_validation
By default, such validations will run every time you call
valid? or save the object. But it is also possible to control when to run these custom validations by giving an
:on option to the validate method, with
:update, or any other custom context you have..
validate :end_time_cannot_be_earlier_than_start_time, on: :create
The new errors you've created will come along with the rest, so in your view, you could use:
<% if @lecture.errors.any? %> <ul> <% @lecture.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul> <% end %>
If you have a background on rails validations, you should be asking yourself the difference between the methods
Basically, you use
validates every time you want to use a default validation from Rails and go with
validate in case you want to use a custom validation method you created.