DEV Community

Cover image for The very basics of Active Record Validations
Eric
Eric

Posted on

The very basics of Active Record Validations

Active Record Validations are a feature of Active Record that gives us the ability to validate data, before it reaches our database. This has a few benefits, but most importantly, we can make sure our database doesn't have unwanted values that could break our application later on.

This guide will attempt to explain how Active Record Validations work, using the Active Records Validations guide as a general reference.


Why use validations?

First of all, why should we use validations? As alluded to before, validations are vital for making sure that all the values in our database are exactly within our expectations.

According to the Ruby on Rails guide:

Model-level validations are the best way to ensure that only valid data is saved into your database. They are database agnostic, cannot be bypassed by end users, and are convenient to test and maintain.

For example, if our database has a column for phone numbers, we know (within reason) that there every number should by 10 digits long (including area code) and by made up of only digits (0-9). So, before creating a new phone number in our database, we should validate it first. If it's invalid, we should reject it.

By making sure our database is valid, we can prevent potential bugs down the line.


Validation Helpers

Validation helpers are the meat of Active Record Validations. These are pre-defined helpers that provide a set of built-in validation rules. We define our validations directly in the model class. With just the built-in validation helpers, we should be able to handle most, if not all, of our necessary validations.

Some of the most useful helpers include: presence, uniqueness, length, comparison, and format. The full list of built-in validation helpers can be found here.

presence

Presence validates that the specified attribute(s) are not empty (nil or an empty string: ""). It is, by far, the simplest helper, as well as, one of the most common helpers.

  class Person < ApplicationRecord
    validates :name, :phone_number presence: true
  end
Enter fullscreen mode Exit fullscreen mode

Now, Active Record won't allow us to create a new person unless there is a name and phone number present. Notice, by the way, that we define our validations directly in each model.

Alternatively, the helper absence does the opposite, ensuring that one or more attributes are absent before saving a new record.

uniqueness

This helper ensures that each value of one or more attributes are unique. One of the most obvious use cases for this would be to ensure every created user had a unique email address.

  class User < ApplicationRecord
    validates :email uniqueness: true
  end
Enter fullscreen mode Exit fullscreen mode

length

This helper is exactly what you think is; it validates the length of the attribute value. There are a few different options available with length, to be as precise as you'd need to be. They are: minimum, maximum, in (or within), and is.

  class Customer < ApplicationRecord
    validates :phone_number length: {is: 10}
  end
Enter fullscreen mode Exit fullscreen mode

For more info about the other options, visit the Ruby on Rails guide, here

comparison

Comparison compares two values and will only validate if the comparison is true. Like length, comparison has several options to choose from. They are: :greater_than, :greater_than_or_equal_to, :less_than, :less_than_or_equal_to, :equal_to, and :other_than.

  class Employee < ApplicationRecord
    validates :hourly_wage comparison: {greater_than_or_equal_to: :minimum_wage}
  end
Enter fullscreen mode Exit fullscreen mode

This examples validates that each employee's wage is never below minimum wage.

format

The last validation helper in this blog is format. Simply put, it validates that a attribute value matches a pre-defined regex pattern. This is useful for making sure an email address or a password follows a specific format.

  class User < ApplicationRecord
    validates :email format: {with: /^\w{2}\w*@\w+\.\w+$/}
  end
Enter fullscreen mode Exit fullscreen mode

format basically allows you to be as flexible with your validations as regular expressions are. The only drawback is having to use regular expressions...


Conditional Validation

To make our validations even more dynamic, Active Record gives us the option to run our validations based on specific conditions that we can define. To add conditions to your validations, simply tag on an :if or an :unless, with the name of a function that evaluates to true or false, in symbol format. I like the example code for custom validations in the Rails docs:

  class Order < ApplicationRecord
    validates :card_number, presence: true, if: :paid_with_card?

    def paid_with_card?
      payment_type == "card"
    end
  end
Enter fullscreen mode Exit fullscreen mode

In the example above, each payment has a "payment_type" associated with it. When using a credit card, we need to use the credit card number to process the payment. It would be wise to validate the credit card number before doing so, to ensure that only cc numbers in a specific format get processed, or else, the payment could fail and that is not ideal.

Adding conditions to our validations greatly enhances our ability to write complex validations in a easy to read manner. With what we now have, we can setup just about any validation we can think of with the Rails built-in validation helpers. But, as an alternative to using the built-in helpers, we can define our own validation methods that will even do the few things the built-in helpers cannot.


Custom Validations

Although the built-in validation helpers that Active Record gives us are awesome, we don't have to use them at all, and, in fact, won't always be enough for the type of validation we need. Instead, we can define our own custom validations to do any sort of validating we could possible desire. Custom validations are generally used for more complex validations while the built-in helpers are efficient for using more straight-forward validations, as their name's imply.

Here's an example of a custom validator that fails if it's not the first of the month:

  class DeliveryOrder < ApplicationRecord
    validate :promise_time_cannot_be_in_the_past

    def promise_time_cannot_be_in_the_past
      errors.add(:promise_time, "cannot be in the past") if self.promise_time < Date.today
    end
  end
Enter fullscreen mode Exit fullscreen mode

The example above is for a delivery order at a restaurant or whatever. There's no way the we could possibly promise to deliver anything that is before the order was placed. This validation ensures we won't have to deal with this later down the line.

This could be written using the built-in validation helpers, but, for whatever reason, I decided I liked this better. Without having to understand the logic behind the validation, it's still obvious that this model is validating that the promise time cannot be in the past, as the name of the validation method eludes to. I guess it's up to preference for which way you'd like to use.


Conclusion

In the article, I've only covered the absolute basics of Active Record Validations. The most glaring area I omitted was error handling. If you'd like to know more (and I encourage you to do so), please check out the main page on validations for Active Record here. The other bigger topic I didn't cover were the "Common Validation Options". They add a few additional ways to make your validations even more dynamic.

Just like everything else in Rails, once your comfortable writing validations and don't need to flip back to the docs regularly, you can write very easy validations in only a few lines of code that are basically human readable. It's pretty incredible.


Resources & Acknowledgements

This article basically cherry-picks and summarizes parts of the guide on Active Record Validations from the Ruby on Rails guides and can be found here.

Besides the Rails guides, these two YouTube tutorials were extremely helpful in understanding how validation worked in Rails:

That's it. Thanks for reading. Go Twins!

Latest comments (0)