Active Record Validations
When I was first learning Ruby, I used a variety of if/else statements to “validate” user input and prevent breakage in my CLI applications. I’m sure you can understand this was not the most efficient way to go about safeguarding your code — though it was prior to learning the ultra-powerful, time and energy-saving, omniscient Active Record. In this post, I’m going to outline the importance of using validations and some of my most used Active Record Validations helpers.
Why Validations?
Validations are used to ensure only valid data is persisted to your database. For example, you’re helping Joe Biden’s 2020 presidential campaign and want to collect the names and phone numbers of all Trump supporters you know. You want to send an automated voice recording that outlines the Trump administration’s obscene failures in handling the COVID-19 pandemic (they deserve to know). Little did you know, Putin found out about your mission and started typing alphabetic characters in your phone number input field. CRAP! Your program breaks because 408-RUSSIA-IS-AWESOME is not a valid phone number.
It’s worth noting: there are many ways to validate data in a Ruby program, but according to Ruby documentation, model-level validations are the most appropriate in most circumstances.
As you may know, saving an object that correlates to a row in your database is a 2 step process in Active Record. First, the object is instantiated.
trump_supporter = User.new("Kanye", 323-290-5589)
Second, the object is saved and persisted in the database.
trump_supporter.save
The moment you save the object to the database, Active Record validations are triggered. This is why it is typically good practice to separate instantiation and saving for better error handling — as opposed to lumping them together using Active Record’s .create
. The following methods will trigger a validation:
.create
.create!
.save
.save!
.update
.update!
The bang versions (with !
) will raise an exception if the record is invalid, whereas the non-bang versions will do the following: .save
and .update
return false
, .create
returns that object. NOTE: it is possible to save an object to a database without triggering our validations. Be sure to use these methods if you wish to trigger validations.
If you need extra error handling help, consider the following methods:
.valid? # triggers validations and returns true if no errors were found in the object, false otherwise.invalid? # inverse of .valid?.errors.messages # returns a collection of errors (if any)
Validation Helpers
Active Record provides us with 13 major validation helpers. You may find it useful to combine these helpers where appropriate, refer to the official documentation for customizing these validations, or create your own validation when you need additional specificity. For the scope of this post, I will only address the simplest implementation of Active Record’s 13 major validation helpers.
acceptance
class User < ApplicationRecord
validates :terms_of_use, acceptance: true
end
This method validates that an HTML form’s checkbox was checked when the form was submitted. Usually used for validating that a user has read the terms of service, agreed to something, etc.
validates_associated
class Author < ApplicationRecord
has_many :posts
validates_associated :posts
end
Use this method when an object has a relationship with another object that also needs to be validated. The method .valid?
will be called on each associated object before continuing. Note: this method works with all association types, but remember to only call within one object to prevent an infinite loop of validations.
confirmation
class User < ApplicationRecord
validates :email, confirmation: true
validates :email_confirmation, confirmation: true
end
This method creates a virtual attribute whose name is the name of the field that has to be confirmed with “_confirmation” appended. It is used when we want to compare and ensure that two text fields are exactly the same (ie a User must type in the same email twice to confirm the correct email). The form within our view might look like this:
<%= text_field :user, :email %>
<%= text_field :user, :email_confirmation %>
exclusion
class User < ApplicationRecord
validates :email, exclusion: {in: %w(yahoo aol hotmail msn)}
end
This validation is used when we want to exclude entries with certain characteristics or text. We use the helper :in
to receive a set of values that will not be accepted for valid attributes. In this example, we don’t want to save any emails from the users who have Yahoo, AOL, Hotmail, and MSN accounts. Welcome to the age of Gmail, my friends.
format
class User < ApplicationRecord
validates :name, format: {with: /\A[a-zA-Z]+\z/, message: "only
allows letters"}
end
This method ensures that the text is formatted as desired. We use the helper :with
to compare our attribute using a regular expression (see Rubular for help with regular expressions). The :message
helper is used here to aid in error messages (see Active Record Validations for additional information on this universal validation helper).
inclusion
class Tshirt < ApplicationRecord
validates :size, inclusion: {in: %w(small medium large)}
end
This validation ensures that the given attribute exists among a given set. In this example, a t-shirt must be size small, medium, or large. Unfortunately, no XS or XL included in these shirts. Again, we use the helper :in
to provide a set of values.
length
class User < ApplicationRecord
validates :bio, length: {maximum: 500}
end
As straightforward as it sounds, the length validation constrains an attribute’s length. Use the helpers :minimum
, :maximum
, :in
(for range), and :is
(for exact value) to assist in the specification.
numericality
class User < ApplicationRecord
validates :age, numericality: true
end
This method ensures the attribute value is numerical. It compares the given value against the following regular expression: /\A[+-]?\d+\z/
. By default, nil values are not accepted. Use the following helpers after numericality:
to customize the validation:
:only_integer
:greater_than
:greater_than_or_equal_to
:equal_to
:less_than
:less_than_or_equal_to
:other_than
:odd
:even
:allow_nil
presence
class User < ApplicationRecord
validates :name, :age, :email, presence: true
end
This validation is used to confirm the existence of attributes. It uses .blank?
to check if the attribute is present, and raises an error if it returns false.
absence
class User < ApplicationRecord
validates :name, :age, :email, absence: true
end
Absence is the inverse of presence and uses .present?
to evaluate if the value is nil or an empty string.
uniqueness
class User < ApplicationRecord
validates :email, uniqueness: true
end
This validation is extremely useful. It cross-checks the database for uniqueness before saving the object. Note: there may still be repeated values if the object is connected to two different databases. You can use :scope
to specify specific attributes that must be unique, see the documentation.
validates_with & validates_each
These validations are more complex and highly customizable. In short, validates_with
uses relationships with other objects to validate attributes. It passes the record to a separate class for validation. Whereasvalidates_each
validates attributes against a block. It does not have a predefined validation function, thus requiring you to customize and specify. Refer to the documentation for additional information and examples.
Hopefully, you’ve gained a better understanding of how and why to use Active Record Validations to prevent breakage and unwanted data in your databases. Thanks for reading! References: Ruby on Rails Active Record Validation.