In the complex world of Rails, what exactly are validations and how can we use them? In this blog, I will give an overview of Rails validators and list some common and useful examples. The Rails Guides state that, "validations are used to ensure that only valid data is saved into your database." As a silly analogy, you can consider validations to be the backend's bouncer, making sure all data is dressed appropriately before entering club database. "Model-level validations are the best way to ensure that only valid data is saved into your database." Client-side validations are possible, but can be insecure or inefficient when used alone. Backend validations ensure that your database is only receiving safe and appropriate data.
What Triggers a Validation?
Validations are implemented when any type of change is made to the database. You will run into this most often when you .save
or .create
, but you can also manually trigger a validation with .valid?
or .invalid?
. There is also a built in validation method on:
that allows you to determine when the validation is triggered:
class User < ApplicationRecord
validates :age, numericality: true, on: :update
end
Common Built-in Validation Methods
Active Record has numerous built-in validation methods that can be employed for common validation needs. Every time these validations fail, they will trigger an error which you can decide to handle in your own way (more on that later). One of the most common built-in validators is presence:
which checks that a certain value has been entered/exists.
class User < ApplicationRecord
validates :username, presence: true
end
*Note that attributes can also be chained together if the same type of validation needs to be provided for each one:
class User < ApplicationRecord
validates :username, :birthday, :full_name, presence: true
end
presence:
is best used when you do not have any other validations in place. If you have another validation in place for the same attribute, presence:
becomes implicit and is not required.
Another common built-in validation is length:
, which checks various length constraints:
class User < ApplicationRecord
validates :name, length: { minimum: 2 }
validates :bio, length: { maximum: 500 }
validates :password, length: { in: 6..20 }
validates :drivers_license, length: { is: 9 }
end
The length:
validator can be used against numerous different constraints such as maximum, minimum, an exact length, or a range.
numericality:
is used to validate an attribute's numeric value.
class User < ApplicationRecord
validates :birthday, numericality: true
validates :phone_number, numericality: { only_integer: true }
end
There are a multitude of numericality:
constraints including: :greater_than
, :greater_than_or_equal_to
, :equal_to
, :less_than
, :less_than_or_equal_to
, :other_than
, :in
(specifies range), :odd
, :even
. These validators can be used for a wide range of purposes and are intuitively named for ease of use.
inclusion:
is another helpful validator that can ensure that a certain value is included. This can be useful if a user is offered a list of options to choose from, and needs to be constrained to that list.
class User < ApplicationRecord
validates :eye_color, inclusion: ["brown", "blue", "hazel", "green", "grey"]
end
Another very common validator is uniqueness:
, this is important for ensure that data, such as usernames or emails are completely unique to a specific user.
class User < ApplicationRecord
validates :username, uniqueness: true
end
Some interesting validations are :allow_blank
and :allow_nil
, these validations are used in a way that bypasses the built-in presence: true
so that if information is entered it can be validated, but it can also be left blank or nil.
class User < ApplicationRecord
validates :bio, length: { minimum: 250 }, allow_blank: true
end
You can also implement conditional validations that are only triggered if a certain condition is met.
class Order < ApplicationRecord
validates :card_number, presence: true, if: :paid_with_card?
def paid_with_card?
payment_type == "card"
end
end
It is also possible to contain your validations within a certain scope.
class Gate < ApplicationRecord
validates :gate_num, uniqueness: { scope: :terminal }
end
Custom Validations
Listed above are just a few of the many built in Active Record validation methods, however, when these aren't enough we also have the ability to build our own custom validations. The syntax is as follows:
class CustomValidator < ActiveModel::Validator
def validate(record)
unless record.species == 'dog'
record.errors.add :species, "Dogs only!"
end
end
end
A common syntax gotcha with custom validations is that custom validations use validate
rather than validates
. When you combine your own custom validations with the myriad of built-in methods, your validating possibilities are nearly endless!
Error Handling
The way you want to handle the errors is a larger topic beyond the scope of this blog as Rails provides you with multiple options, however, I would like to highlight the built-in message:
method as it is incorporated directly into your validations.
class User < ApplicationRecord
validates :username, presence: { message: "must be given please" }
end
Conclusion
Rails validations are a simple and effective way to ensure you are allowing appropriate and safe data into your database. You can choose from numerous built-in validator methods for your most common validation needs or you can use your creative freedom and design custom validations for unique needs.
Top comments (0)