Creating Custom Validations in Rails

by Ícaro Falcão


Validated


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.


Creating a validation

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 :create, :update, or any other custom context you have..

  validate :end_time_cannot_be_earlier_than_start_time, on: :create

Showing the error messages on the view

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 %>

Difference between validate and validates

If you have a background on rails validations, you should be asking yourself the difference between the methods validate and validates 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.


Leave a Comment

(Other users will not see it)
* Required fields