Validation HelpersFurther information on pre-defined helpers to validate data for model attributes

In this deets, I will further explain the different types of Validation Helpers (previously explained in the Validation Helpers Demo) and how to use them. Model Validations are used to ensure that only valid data are saved to the database. We can use validation helpers that are provided by rails to easily validate data, or we can build our own custom validations.

To explain the different types of validation helpers to a model class, we will be adding a validation to the existing Review model class (depicted in Figure 1) from the movie-review base app branch.

A class diagram depicting a Review model class with the following attributes: a title string, a score decimal number, body text, a genre string, a link string, a release date, and a review date

Figure 1. The Review model class.

Length Validation

To add a length validation for the body attribute of the Review model class, we add a validates declaration to the class definition (found in app/models/review.rb), like this:

app/models/review.rb
1
# == Schema Information
2
#
3
# Table name: reviews
4
#
5
#  id           :bigint           not null, primary key
6
#  body         :text
7
#  genre        :string
8
#  link         :string
9
#  release_date :date
10
#  review_date  :date
11
#  score        :decimal(, )
12
#  title        :string
13
#  created_at   :datetime         not null
14
#  updated_at   :datetime         not null
15
#
16
class Review < ApplicationRecord
17
18
  validates :body, length: { minimum: 50 }
19
20
end

Breaking down this line of code, the call to validates adds the specified validation to the model class. The :body argument specifies that the validation will apply to the body attribute of the class. The length: { minimum: 50 } argument specifies that the length validation helper will be applied to the attribute. For a string attribute, like body, this validation ensures that the attribute is at least 50 characters long.

Numericality Validation

To add a numericality validation for the score attribute of the Review model class, we add a validates declaration to the class definition (found in app/models/review.rb), like this:

app/models/review.rb
1
# == Schema Information
2
#
3
# Table name: reviews
4
#
5
#  id           :bigint           not null, primary key
6
#  body         :text
7
#  genre        :string
8
#  link         :string
9
#  release_date :date
10
#  review_date  :date
11
#  score        :decimal(, )
12
#  title        :string
13
#  created_at   :datetime         not null
14
#  updated_at   :datetime         not null
15
#
16
class Review < ApplicationRecord
17
18
  validates :score, numericality: true
19
20
end

Breaking down this line of code, the call to validates adds the specified validation to the model class. The :score argument specifies that the validation will apply to the score attribute of the class. The numericality: true argument specifies that the numericality validation helper will be applied to the attribute. This validation ensures that the attribute is a numeral, like 7 and not an alphabetic string such as seven.

Inclusion Validation

To add a inclusion validation for the genre attribute of the Review model class, we add a validates declaration to the class definition (found in app/models/review.rb), like this:

app/models/review.rb
1
# == Schema Information
2
#
3
# Table name: reviews
4
#
5
#  id           :bigint           not null, primary key
6
#  body         :text
7
#  genre        :string
8
#  link         :string
9
#  release_date :date
10
#  review_date  :date
11
#  score        :decimal(, )
12
#  title        :string
13
#  created_at   :datetime         not null
14
#  updated_at   :datetime         not null
15
#
16
class Review < ApplicationRecord
17
18
  validates :genre, inclusion: { in: ['Action', 'Science Fiction', 'Drama', 'Horror', 'Comedy', 'Musical'] }
19
20
end

Breaking down this line of code, the call to validates adds the specified validation to the model class. The :genre argument specifies that the validation will apply to the genre attribute of the class. The inclusion: { in: ['Action', 'Science Fiction', 'Drama', 'Horror', 'Comedy', 'Musical'] } argument specifies that the inclusion validation helper will be applied to the attribute. This validation uses in: to specify an array of allowed values and ensures that the attribute is only one of those values.

Custom Validation

To add a custom validation to the Review model class, we follow these steps to add a validate declaration and custom validation method to the class definition (found in app/models/review.rb).

Create custom method. In app/models/review.rb, add a custom method to hold the validation logic with an appropriate name, like so:

app/models/review.rb
1
# == Schema Information
2
#
3
# Table name: reviews
4
#
5
#  id           :bigint           not null, primary key
6
#  body         :text
7
#  genre        :string
8
#  link         :string
9
#  release_date :date
10
#  review_date  :date
11
#  score        :decimal(, )
12
#  title        :string
13
#  created_at   :datetime         not null
14
#  updated_at   :datetime         not null
15
#
16
class Review < ApplicationRecord
17
18
  def review_date_must_be_after_release_date
19
    
20
  end
21
22
end

Add statement to apply error to attribute unless value passes constraint. In this case, the constraint is that the object’s review_date must come after the release_date. Unless the constraint check passes, we want to add a statement that applies an Active Record error to the object with an appropriate message, like so:

app/models/review.rb
1
# == Schema Information
2
#
3
# Table name: reviews
4
#
5
#  id           :bigint           not null, primary key
6
#  body         :text
7
#  genre        :string
8
#  link         :string
9
#  release_date :date
10
#  review_date  :date
11
#  score        :decimal(, )
12
#  title        :string
13
#  created_at   :datetime         not null
14
#  updated_at   :datetime         not null
15
#
16
class Review < ApplicationRecord
17
18
  def review_date_must_be_after_release_date
19
    errors.add(:review_date, "must come after release date") unless review_date.after?(release_date)
20
  end
21
22
end

Breaking down this line of code, we are using the add method to add an error message for an attribute to the object’s errors collection. In the call to add, the :review_date parameter indicates which attribute the error pertains to, and the "must come after release date" parameter is the custom error message we set on that attribute. The unless keyword is used as a modifier such that the preceding argument is executed if the following argument is not true. In this case, the error will be added if an object’s review_date is NOT after the release_date. We use the after method to compare the two Date values.

Add validation declaration to model class. Although we have written the validation function, we still need to tell Rails to run it when validating an object. To do this, we need to add a validate declaration for the custom method, like so:

app/models/review.rb
1
# == Schema Information
2
#
3
# Table name: reviews
4
#
5
#  id           :bigint           not null, primary key
6
#  body         :text
7
#  genre        :string
8
#  link         :string
9
#  release_date :date
10
#  review_date  :date
11
#  score        :decimal(, )
12
#  title        :string
13
#  created_at   :datetime         not null
14
#  updated_at   :datetime         not null
15
#
16
class Review < ApplicationRecord
17
18
  validate :review_date_must_be_after_release_date
19
20
  def review_date_must_be_after_release_date
21
    errors.add(:review_date, "must come after release date") unless review_date.after?(release_date)
22
  end
23
24
end

Further Reading

For more info on model validations and validation helpers, see the Rails Guides.