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.
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:
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:
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:
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:
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:
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:
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.