# Validation
Active Record allows you to validate the state of a model before it gets written into the database. There are several methods that you can use to check your models and validate that an attribute value is not empty, is unique and not already in the database, follows a specific format, and many more.
Writing Validation Logic
class Person < ApplicationRecord validates :name, presence: true end
1
2
3The key word
validates
precedes the attributes to be validated (separated by commas), and is succeeded by thepresence
validator, assigned the conditiontrue
. Thepresence
validator checks that the attribute is notnil
or an emptystring
.We will discuss about other validator latter.
Before saving an Active Record object, Rails runs your validations. If these validations produce any errors, Rails does not save the object.
You can also run these validations on your own.
[valid?](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/Validations.html#method-i-valid-3F)
triggers your validations and returns true if no errors were found in the object, and false otherwise. As you saw aboveirb> Person.create(name: "John Doe").valid? => true irb> Person.create(name: nil).valid? => false
1
2
3
4[invalid?](https://api.rubyonrails.org/v7.0.4/classes/ActiveModel/Validations.html#method-i-invalid-3F)
is the inverse ofvalid?
. It triggers your validations, returning true if any errors were found in the object, and false otherwise.Working With Error Messages
The
[valid?](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/Validations.html#method-i-valid-3F)
and[invalid?](https://api.rubyonrails.org/v7.0.4/classes/ActiveModel/Validations.html#method-i-invalid-3F)
methods only provide a summary status on validity. However you can dig deeper into each individual error by using various methods from the[errors](https://api.rubyonrails.org/v7.0.4/classes/ActiveModel/Validations.html#method-i-errors)
collection.class Person < ApplicationRecord validates :name, presence: true, length: { minimum: 3 } end
1
2
3irb> person = Person.new(name: "JD") irb> person.valid? => false irb> person.errors[:name] => ["is too short (minimum is 3 characters)"] #where irb> person.errors.where(:name) => [ ... ] # all errors for :name attribute irb> person.errors.where(:name, :too_short) => [ ... ] # :too_short errors for :name attribute #error object irb> error = person.errors.where(:name).last irb> error.attribute => :name irb> error.type => :too_short irb> error.options[:count] => 3 irb> error.message => "is too short (minimum is 3 characters)" irb> error.full_message => "Name is too short (minimum is 3 characters)" #error size irb> person.errors.size => 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35Skipping Validations
The following methods skip validations, and will save the object to the database regardless of its validity. They should be used with caution.
decrement!
decrement_counter
increment!
increment_counter
insert
insert!
insert_all
insert_all!
toggle!
touch
touch_all
update_all
update_attribute
update_column
update_columns
update_counters
upsert
upsert_all
Note that
save
also has the ability to skip validations if passedvalidate: false
as an argument. This technique should be used with caution.save(validate: false)
Available Validation Rule
presence: This method simply checks that the specified attribute(s) are not empty. In the below example the validation is ensuring that the name, login and email attributes are not empty.
class Person < ApplicationRecord validates :name, :login, :email, presence: true end
1
2
3length: This method checks the length of the given attribute’s value. It provides several different constraints: minimum, maximum, is and in. See below for an example.
class Person < ApplicationRecord validates :name, length: { minimum: 2 } validates :bio, length: { maximum: 500 } validates :password, length: { in: 6..20 } validates :registration_number, length: { is: 6 } end
1
2
3
4
5
6uniqueness: This method check that a given attribute’s value is unique. In the below example the validation will check that value entered for the email attribute is unique. This method also allows you to add scope to specify one or more attributes to use as a limit for the uniqueness.
class Account < ApplicationRecord validates :email, uniqueness: true end
1
2
3The validation happens by performing an SQL query into the model's table, searching for an existing record with the same value in that attribute.
There is a
:scope
option that you can use to specify one or more attributes that are used to limit the uniqueness check:class Holiday < ApplicationRecord validates :name, uniqueness: { scope: :year, message: "should happen once per year" } end
1
2
3
4Should you wish to create a database constraint to prevent possible violations of a uniqueness validation using the
:scope
option, you must create a unique index on both columns in your database.There is also a
:case_sensitive
option that you can use to define whether the uniqueness constraint will be case sensitive, case insensitive, or respects default database collation. This option defaults to respects default database collation.class Person < ApplicationRecord validates :name, uniqueness: { case_sensitive: false } end
1
2
3inclusion: This method checks that the given attribute’s values are included in a given set. Below, the validation method is checking that the attribute size includes either small, medium or large.
class Coffee < ApplicationRecord validates :size, inclusion: { in: %w(small medium large), message: "%{value} is not a valid size" } end
1
2
3
4Another useful way to utilize the inclusion validation method is to check for true/false values. See below for an example:
validates :boolen_field_name, inclusion: {in: [true, false]}
1exclusion:
This method checks that the given attribute’s values are NOT included in a given set. In the example below, the validation is checking that in the subdomain attribute, “www”, “us”, “ca” and “jp” are not included. If a user tried to set the subdomain’s attribute as any of those strings, they would receive an error.
class Account < ApplicationRecord validates :subdomain, exclusion: { in: %w(www us ca jp), message: "%{value} is reserved." } end
1
2
3
4format:
This method checks that the given attribute’s values match a given regular expression, which is specified using the
:with
option. Below is an example.class Product < ApplicationRecord validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/, message: "only allows letters" } end
1
2
3
4numericality:
****This method checks that the given attribute has only numeric values. If you’d like to further specify only integral numbers, you would then follow numericality with :only_integer: true. In the below example the validation is checking that the points attribute is a number (integer or float). The next validation is checking that the games_played attribute is specifically an integer number.
class Player < ApplicationRecord validates :points, numericality: true validates :games_played, numericality: { only_integer: true } end
1
2
3
4Besides
:only_integer
, this helper also accepts the following options to add constraints to acceptable values::greater_than
- Specifies the value must be greater than the supplied value. The default error message for this option is "must be greater than %{count}".:greater_than_or_equal_to
- Specifies the value must be greater than or equal to the supplied value. The default error message for this option is "must be greater than or equal to %{count}".:equal_to
- Specifies the value must be equal to the supplied value. The default error message for this option is "must be equal to %{count}".:less_than
- Specifies the value must be less than the supplied value. The default error message for this option is "must be less than %{count}".:less_than_or_equal_to
- Specifies the value must be less than or equal to the supplied value. The default error message for this option is "must be less than or equal to %{count}".:other_than
- Specifies the value must be other than the supplied value. The default error message for this option is "must be other than %{count}".:in
- Specifies the value must be in the supplied range. The default error message for this option is "must be in %{count}".:odd
- Specifies the value must be an odd number if set to true. The default error message for this option is "must be odd".:even
- Specifies the value must be an even number if set to true. The default error message for this option is "must be even".
You can view all the validator options below
Active Record Validations - Ruby on Rails Guides (opens new window)
Custom Validation Rule
Custom Validators
Custom validators are classes that inherit from
[ActiveModel::Validator](https://api.rubyonrails.org/v7.0.4/classes/ActiveModel/Validator.html)
. These classes must implement thevalidate
method which takes a record as an argument and performs the validation on it. The custom validator is called using thevalidates_with
method.class MyValidator < ActiveModel::Validator def validate(record) unless record.name.start_with? 'X' record.errors.add :name, "Need a name starting with X please!" end end end class Person include ActiveModel::Validations validates_with MyValidator end
1
2
3
4
5
6
7
8
9
10
11
12The easiest way to add custom validators for validating individual attributes is with the convenient
[ActiveModel::EachValidator](https://api.rubyonrails.org/v7.0.4/classes/ActiveModel/EachValidator.html)
. In this case, the custom validator class must implement avalidate_each
method which takes three arguments: record, attribute, and value. These correspond to the instance, the attribute to be validated, and the value of the attribute in the passed instance.class EmailValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i record.errors.add attribute, (options[:message] || "is not an email") end end end class Person < ApplicationRecord validates :email, presence: true, email: true end
1
2
3
4
5
6
7
8
9
10
11Custom Methods
You can also create methods that verify the state of your models and add errors to the
errors
collection when they are invalid. You must then register these methods by using the[validate](https://api.rubyonrails.org/v7.0.4/classes/ActiveModel/Validations/ClassMethods.html#method-i-validate)
class method, passing in the symbols for the validation methods' names.You can pass more than one symbol for each class method and the respective validations will be run in the same order as they were registered.
The
valid?
method will verify that theerrors
collection is empty, so your custom validation methods should add errors to it when you wish validation to fail:class Invoice < ApplicationRecord validate :expiration_date_cannot_be_in_the_past, :discount_cannot_be_greater_than_total_value def expiration_date_cannot_be_in_the_past if expiration_date.present? && expiration_date < Date.today errors.add(:expiration_date, "can't be in the past") end end def discount_cannot_be_greater_than_total_value if discount > total_value errors.add(:discount, "can't be greater than total value") end end end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16By 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 thevalidate
method, with either::create
or:update
class Invoice < ApplicationRecord validate :active_customer, on: :create def active_customer errors.add(:customer_id, "is not active") unless customer.active? end end
1
2
3
4
5
6
7Displaying Errors in frontend
<% if @article.errors.any? %> <h3>The following errors prevented the article from being saved:</h3> <ul> <% @article.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> <% end %>
1
2
3
4
5
6
7
8Displaying message using flash
Now, what if the new article is saved successfully? How could we tell the user this?
Back in the
articles_controller.rb
file:def create @article = Article.new(params.require(:article).permit(:title, :description)) if @article.save flash[:notice] = "This article was saved successfully" redirect_to @article else render 'new' end end
1
2
3
4
5
6
7
8
9Above we say if the article is saved successfully to the database, we use flash (opens new window) to pass a temporary message from the
create
action to theshow
action. Theshow
action will automatically pass the massage to its view. However, we can do something like this:In the
application.html.erb
file:<body> <% flash.each do |name, msg| %> <%= msg %> <% end %> <%= yield %> </body>
1
2
3
4
5
6
← Model Relationships →