DEV Community

jhalfman
jhalfman

Posted on

Active Record Validations

What are Active Record Validations?

When creating databases, we can create rules for our models when running migrations--these rules will always be run when interacting with the database. Alternatively, Active Record allows us to specify rules to be checked only when we are saving data to our databases. These rules are called validations, and they are called any time the database is altered. The validations are defined in our model files, and they are essential in preventing bad data from being saved to our databases.

How do we get validations?

Active Record provides us with some helper methods by inheriting them from ActiveRecord::Base. The main method is #validates, which is utilized in a model and along with an attribute to be validated and the way to validate that attribute. We can also utilize #validate (without the 's') to write our own custom validations, which will be highlighted later.

Image description
Image description

What triggers validation?

To prevent bad date from entering the database, Active Record runs our validations before any database activity. This means validations are run before objects are created, saved, or updated using the #create, #save, and #update methods, respectively. The only way to run validations without causing database activity is with the #valid? method. The #valid? method can be called on an object and will return true if no errors are found in the object and false if not. Validations are not run when simply instantiating a new object without saving to the database using the #new method. There are several other methods that skip validations and therefore must be used with caution. Some examples of these methods are #insert, #decrement!, and #update_column. The validations can be bypassed in the #save method by passing validate: false as an argument (this is also a risky technique).

The following Person class has a name:string and age:integer, and the validation specifies that the name attribute must be present, using validates :name, presence: true

Image descriptionAs you can see, we are allowed to use #new to make a new Person object, even though the name attribute is not present. However, when #save is attempted, the record is not saved. When checking the new Person with #valid?, we see that the method returns false.
Image descriptionHere, #create is attempted while still missing the required :name attribute. We can see that the new object has not been created (:id is returned as nil), and #valid? returns false.

Image descriptionFinally, a valid Person is created, and #valid? returns true.

Validation success and failure

If a database method is called and all validations are successful, the method will execute successfully (an object will either be created/saved to the database or updated appropriately). As expected, if the validations fail, Active Record prevents the bad data from being stored! Just what we wanted--but how do we know what happened, and how would we show the user that submitted the incompatible data? Lucky for us, Active Record communicates this to us, too.

Validation errors

When a validation fails, Active Record adds an error to the #errors instance method. All errors can be accessed by calling errors.messages on an instance of a model, and errors of specific attributes can be accessed by calling #errors with a specific attribute (errors[:attribute]). We can also use errors.full_messages for an array of errors that are formatted for clearer rendering to a front-end user.
Image descriptionA validation was added to the person model to require numbers for the :age attribute, validates :age, numericality: true. We can view the default error messages by accessing #errors on the instance of the class.

Image description#errors.full_messages formats the message by combining the attribute name and the corresponding message into one string.

Throwing Exceptions

Aside from adding an error message, Active Record will not do anything else for us unless we ask. This is done by adding a ! to the end of the database method: #create!, #update!, #save!. This will tell Active Record to also raise an exception when the validation fails. We can then rescue the error if it is useful to our control flow.
Image descriptionThe RecordInvalid exception can be rescued and used to send the user error messages.

Validation helpers

Some of the most commonly used validations are supplied for us by Active Record. After typing validates :attribute in a model, we can utilize these helpers on the same line. The helpers can all be declared with the :on and :message values. The :on option accepts :create and :update and specifies when a validation is called. The :message option allows you to create your own custom error message instead of the default message provided when a validation is failed.

Image descriptionThis Person model would require a value for :name only when using #create, and the message for an invalid age has been customized!

Here is a list of common validation helpers provided by rubyonrails.org, with a few examples provided for some of them:

  1. acceptance
  2. validates_associated
  3. confirmation
  4. comparison
  5. exclusion
  6. format
  7. inclusion
  8. length
  9. numericality
  10. presence
  11. absence
  12. uniqueness
  13. validates_with
  14. validates_each

uniqueness

Using uniqueness: true validates if a value exists in the database before the object gets saved. This can also be used along with the :scope option to limit the uniqueness check to a different attribute.

presence/absence

Using presence: true is a simple way to require that an attribute is not blank. If the value is nil or a blank string, the validation fails. absence: true does the opposite, checking if the attribute is nil or blank.

numericality

Using numericality: true verifies that an attribute is only using numeric values. This validation can also be used with a series of additional constraints to further specify what sort of value is acceptable. A few of these are: :greater_than requires the value to be greater than the defined value; :odd/:even can be set to true to specify a value must be odd or even; :in checks that the value exists in the provided range.

exclusion

Using exclusion: {in: } accepts a set of values that are prohibited from being used in the specified attribute.

Image descriptionHere is an attempt to create a Person with a reserved name, and an age that is neither greater than the specified value nor odd.

Custom validation

As mentioned earlier, we can also use #validate to create our own custom validation. #validate is used by defining a separate function within the model to perform an action, and then calling validate :custom_validation. When performing a validation check, Active Record looks to see if there are any error messages in the errors collection. Using this, we can add an error when our custom validation fails in order to cause #valid? to return false.

Image description

Image description
This shows a custom method that checks to see if :name is in all caps. If it is, an error message is added which causes the validation to fail.

Further Use

There are a huge amount of ways to continue to utilize the validations capabilities provided by Active Record. The methods can be customized and specialized to meet an endless amount of needs. Visit https://guides.rubyonrails.org/active_record_validations.html for full documentation on active record validations, and for further examples of ways to utilize the tool.

Top comments (0)