Understanding ActiveRecord Validations with Ruby on Rails

Image for post
Image for post
photo credit Cookie the Pom

Validations are essential to a Rails application. They allow us to control the information that is being saved into our database. They are given to us through Active Record and allow us to be thoughtful about how we save data. There are many ways that you can control database information. You can use client-side validations and controller-level validations, but the Rails team favors model-level validations for a multitude of reasons. They are database agnostic, meaning that they can be used with various systems and are not customized for a single system. They cannot be bypassed by users. They are convenient to test and maintain.

Validations are triggered on .save or .valid?. In other words, validations are only triggered when we are attempting to persist data to the database. Active Record is doing a lot for us behind the scenes. It gives us a method called new_record? that checks the database to see if the record exists in our database and will return true if it’s new. Below is an example of the console output testing this method:

$ rails console>> b = Book.new(name: "A Tale of Two Cities")=> #<Book id: nil, name: "A Tale of Two Cities", created_at: nil, updated_at: nil>>> b.new_record?=> true>> b.save=> true>> b.new_record?=> false

Creating and updating objects will not trigger the validation until a save attempt is made in which case the database will return an invalid response and will not persist the data.

The following methods all trigger validations: (note the bang versions will raise an exception if invalid while the others will simply not save the data)

  • create
  • create!
  • save
  • save!
  • update
  • update!

In this blog, we will explore:

  • How to use the built in validations provided by Active Record
  • Custom Validations
  • Using the errors that validations give us

The built in validations are very useful. Out of the box we are given the following validations (each of the following links to the Ruby Guide):

Each of these validations takes in an unlimited number of attributes so you can use a single line of code to define validations for multiple attributes.

Acceptance is used in specific cases where you need to determine if a User has checked a box or read some text, typically when signing up for a webpage, agreeing to terms of services, etc.. You do not have to have a field for it in your database as this validation will create a “virtual attribute” for you.

class User < ApplicationRecord  validates :terms_of_service, acceptance: trueend

Validates_Associated is used when you need to ensure that a model is associated with another model. Be careful, do not use it on both sides of an association as it will cause an infinite loop.

class List < ApplicationRecord  has_many :tasks  validates_associated :tasks end

Confirmation is used when you need two input boxes to contain the exact same information. This is especially useful when asking for password confirmation. This creates a “virtual attribute” with _confirmation appended to the attribute name (password_confirmation). The check is only performed if a confirmation is present, so it is important to also validate for presence(covered later).

class User < ApplicationRecord  validates :password, confirmation: true
validates :password, presence: true
end

You could then use something like this in your views:

<%= text_field :user, :password %><%= text_field :user, :password_confirmation %>

Confirmation allows for a :case_sensitive option that can be set to true or false which can be passed in after the validation in curly braces ({}).

class User < ApplicationRecord  validates :password, confirmation: { case_sensitive: false }end 

Exclusion is used to ensure that certain attributes are not included in the set. It has an option that allows you to pass :in which lets you reserve things to disallow any one from saving data that includes certain characters.

class Account < ApplicationRecord  validates :subdomain, exclusion: { in: %w(www us ca jp),  message: "%{value} is reserved." }end

Format is used to determine if a value input is in the correct format to be saved to the database. It allows you to pass in an option :with. You can also pass in a :without option that will require that an attribute does not have that formatting.

class User < ApplicationRecord
validates :name, format: { with: /\A\w{6,10}\z/ }
end

Inclusion validates that the attribute is in a given set. It has an option of in: that allows you to set the values that will be accepted.

class Shirt < ApplicationRecord  validates :size, inclusion: { in: %w(small medium large),  message: "%{value} is not a valid size" }end

Length validates that an attribute’s value is a certain length. This can be useful with passwords or to ensure that a text body is limited to a certain number of characters.

class Post < ApplicationRecord  validates :title, length: { minimum: 2 }  validates :body, length: { maximum: 500 }end

or

class Post < ApplicationRecord  validates :username, length { minimum: 2 }  validates :password, length: { in: 6..20 }  validates :registration_number, length: { is: 6 }end 

Numericality is used to ensure that user input is only an integer or float. You can optionally set only_integer: true if you do not want it to be converted to a float. The only_integer helper also provides further constraints that can be added. These are as follows:

  • greater_than
  • greater_than_or_equal_to
  • equal_to
  • less_than
  • less_than_or_equal_to
  • other_than
  • odd
  • even
class Stock < ApplicationRecord  validates :stock_qty, :numericality => { :only_integer => true,
:greater_than_or_equal_to => 0 }
end

Presence validates that attributes cannot be empty. This is important in new forms to ensure that nil data is not being saved to the database. If we are saving data that is incomplete this can break forms and views later on.

class Person < ApplicationRecord  validates :name, :username, :email, presence: trueend

Uniqueness validates that an attribute is not the same as an object that is already stored in the database. This is important to use for user data to ensure that a user is not signing up multiple times or that username’s are not duplicated.

class User < ApplicationRecord  validates :email, uniqueness: true  validates :username, uniqueness: true end

Uniqueness has a scope option that can be used to limit the check of the data.

class Holiday < ApplicationRecord  validates :name, uniqueness: { scope: :year,  message: "only happens once per year" }end

Custom validations inherit from ActiveRecord::Validator. When writing a custom validation we need to call to it using the validates_with helper. These validation classes must utilize the validate method which takes the record as an argument and performs the validation on it.

class CustomValidator < ActiveModel::Validator

def validate(record)
if ! (record.valid...)

record.errors[:base] << "Custom Error message"

end
end end class Post < ApplicationRecord validates_with CustomValidator end

When trying to validate specific attributes you can use the validate_each method. The validate_each method takes in three arguments: record, attribute, value. These belong to the instance, the attribute we are trying to validate, and the value of said attribute.

class EmailValidator < ActiveModel::EachValidator  def validate_each(record, attribute, value)    unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i    record.errors[attribute] << (options[:message] || "is not an
email"
)
end end endclass Person < ApplicationRecord validates :email, presence: true, email: trueend

Validations in Rails give us access to errors.full_messages or errors.to_a — both of these provide us with errors that we can show to the user to assist them in correctly adding data to the database. Each validation has a unique error that can be rendered. For example:

class Person < ApplicationRecord  validates :name, presence: true, length: { minimum: 3 }end# in consoleperson = Person.newperson.valid? # => falseperson.errors[:name]# => ["can't be blank", "is too short (minimum is 3 characters)"] 

These errors can be extremely useful to us. When asking users to input data we want to be sure that we are getting the correct data and that the user also understands how to properly fill out the forms we have provided them. It would be very frustrating for a user to be unable to persist data because they do not know what that data should look like. Therefore, we want to be sure to call to these errors.full_messages in our views and re-render the form/page to the user with the error messages. We lose access to these errors on redirect so it is imperative to render the form/page again.

<% if model.errors.any? %> <div id="error_explanation ">       <%= pluralize(model.errors.count, "error") %>   

prohibited this from being saved:

<ul><% model.errors.full_messages.each do |msg| %>

<li><%= msg %></li>
<% end %> </ul> <% end %>
Image for post
Image for post

I hope this was helpful in understanding the importance of validations in Rails and the many ways that they can assist in keeping that database clean and functional.

References:

Active Record Validations

Written by

Full Stack Developer. Designs solutions combining analytical, technical, and problem-solving skills to make ideas come to life.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store