Active Record validations allow you control data saved to your database and help prevent headaches down the road! From a properly formatted date... to making sure a username is unique upon signup, Active Record provides pre-defined helpers you can use to make sure the input meets requirements. Let's take a look at some common validations that help keep your database tables cohesive.
There are a few ways you can provide validations for data but the most efficient way is by using them server side at the model level. Model level validations are database agnostic (compatible with any database), cannot be bypassed by the end users, and are easy to test and maintain. Now that being said, there are benefits to having both server side and client side validation! It can improve application performance by not having to process invalid data. To get the grasp of the differences and benefits of using both, check out this article.
So where in the process do validations take place? Active Record validations allow us to validate data before being stored in our database. They are triggered when using the create
, save
and update
methods. The validations run right before the SQL commands are sent to the database, so if any fail, Active Record will not perform the SQL INSERT or UPDATE. If the record is invalid, create
returns the object, save
and update
return false, and all the bang versions of these methods raise an exception. (e.g. create!
) Here is a list of methods that skip validations.
The presence
helper makes sure that the specified attribute(s) are not empty. A nil
or a blank string value will be invalid.
class Student < ApplicationRecord
validates :name, :grade, presence: true
end
In the example above, Active Record will not create a new student without a name and grade. The direct opposite helper is absence
, which will make sure the attribute(s) are nil or a blank string.
It's common to need a unique attribute value, for example, you would probably want unique usernames for your application. Here the helper will search the Student table for existing records to determine the uniqueness of the specified attribute.
class Student < ApplicationRecord
validates :username, uniqueness: true
end
There is a scope
option available with uniqueness
that allows you to specify additional columns to consider when checking for uniqueness.
class Assignment < ApplicationRecord
belongs_to :student
validates :title, presence: true, uniqueness: { scope: :student_id }
end
This is requiring that the combination of the assignment title
and student_id
be unique. Other student accounts can have the same assignment title
, but it will not be duplicated in any student account.
There are many options for validating the length of an attribute's value...
class Student < ApplicationRecord
validates :name, length: { minimum: 5 } # sets minimum
validates :bio, length: { maximum: 500 } # sets maximum
validates :password, length: { in: 5..12 } # provides a range
validates :library_number, length: { is: 8 } # must match length
end
You can customize error messages using default error messages :wrong_length
, :too_long
, and :too_short
. Note that the default messages are plural(e.g. 'characters'). There is also %{count}
which is a placeholder for the number constraint being used.
class Student < ApplicationRecord
validates :bio, length: { maximum: 500,
too_long: "%{count} characters is the maximum allowed" }
# sets maximum, custom message specifying the character limit
end
You might need to format
strings for your application, in which case, you can test if they match specifications given using the :with
option. This can come in handy to check that a valid email address is entered or if you wanted to exclude numbers/symbols.
class Student < ApplicationRecord
validates :name, format: { with: /\A[a-zA-Z]+\z/,
message: "only allows letters" }
end
This helper ensures attributes have only numeric values. To specify only integer numbers, use :only_integer
set to true. The numericality
helper does not allow nil
values. To allow for this option you can use allow_nil: true
.
class Test < ApplicationRecord
validates :score, numericality: { only_integer: true }
end
:greater_than
:greater_than_or_equal_to
:equal_to
:less_than
:less_than_or_equal_to
:other_than
(value must be other than the supplied value)
:in
(value must be in the supplied range)
:odd
:even
This helper compares two values and will validate only if the comparison is true. Below the validation requires the race :start_time
to be :less_than
the :finish_time
or it will be invalid.
class Race < ApplicationRecord
validates :start_time, comparison: { less_than: :finish_time }
end
These are the supported options for comparison:
:greater_than
:greater_than_or_equal_to
:equal_to
:less_than
:less_than_or_equal_to
:other_than
(value must be other than the supplied value)
Before I wrap up, I wanted to demonstrate some of the many ways you can check your validations using the rails console
:
class User < ApplicationRecord
validates :username, presence: true
end
$ rails console
>> user1 = User.create(username: "")
=> #<User id: nil username: nil, created_at: nil, updated_at: nil>
#create returns the object with no id assigned, meaning it did not save to the database
>> user1.save
=> false
>> user2 = User.create!(username: "")
=> ActiveRecord::RecordInvalid (Validation failed: Username can't be blank)
#using the bang operator with create raises an exception when invalid
>>user3 = User.create(username: "Thor").valid?
=> true
>>user1.valid?
=> false
#you can use the opposite .invalid?
>>user1.errors.any?
=> true
>>user1.errors.full_messages
=> ["Username can't be blank"]
I only scratched the surface on what's possible using Active Record validations! Explore more pre-built helpers, conditional and custom validations, and validation errors.
You can connect with me on LinkedIn & GitHub
Resources: Rails Guides - Active Record Validations
Top comments (0)