Rails: Validation

What is Validation?

In Rails, validations are used in order to ensure valid data is passed into the database. Validations can be used, for example, to make sure a user inputs their name into a name field or a username is unique. Protecting your database from invalid data is key in preventing confusing errors in the future. In the MVC architecture, (in most circumstances), the model is the best place to enter validations. This way the user would have no way of interfering with the validation code itself. As per usual, Rails is magical and provides built in validation methods out of the box. If the built in methods aren’t cutting it, Rails will also allow the option to create custom validations.

How to Use Validation

The method “validates” takes two arguments, the name of the attribute you’d like to validate and how you would like to validate that attribute. It’s also important to note that database activity triggers validation, meaning if you simply instantiate using the new method, there is no interaction with the database yet and thus the validation(s) won’t run. So, to validate, you can simply use the valid? method.

Below is a quick example of using the valid? method from learn.co. In the Person class, the validation method is ensuring that the name attribute is present upon instantiation. When creating the person instance with the name “John Doe” and calling valid?, you’ll see the result is true as no errors could be found. With an empty name attribute, valid? returns false.

Learn.co “ActiveRecord Validations”

Validation Helpers + Examples

Luckily, as I mentioned before, Rails provides a plethora of built in validation methods. Here is a list of some of the validations methods I found useful:

presence- This method simply checks that the specified attribute(s) are not empty. In the below example the validation is ensuring that the name, login and email attributes are not empty.

https://guides.rubyonrails.org/v5.1/active_record_validations.html

length-This method checks the length of the given attribute’s value. It provides several different constraints: minimum, maximum, is and in. See below for an example.

https://guides.rubyonrails.org/v5.1/active_record_validations.html

uniqueness-This method check that a given attribute’s value is unique. In the below example the validation will check that value entered for the email attribute is unique. This method also allows you to add scope to specify one or more attributes to use as a limit for the uniqueness.

inclusion- This method checks that the given attribute’s values are included in a given set. Below, the validation method is checking that the attribute size includes either small, medium or large.

https://guides.rubyonrails.org/v5.1/active_record_validations.html

Another useful way to utilize the inclusion validation method is to check for true/false values. See below for an example:

https://guides.rubyonrails.org/v5.1/active_record_validations.html

exclusion- This method checks that the given attribute’s values are NOT included in a given set. In the example below, the validation is checking that in the subdomain attribute, “www”, “us”, “ca” and “jp” are not included. If a user tried to set the subdomain’s attribute as any of those strings, they would receive an error.

https://guides.rubyonrails.org/v5.1/active_record_validations.html

format- This method checks that the given attribute’s values match a given regular expression, which is specified using the :with option. Below is an example.

FYI: the \A and \z are regular expressions that are essentially indicating to match the beginning and end of the string to the arguments passed in between, which in this case is: [a-zA-Z]. The [a-zA-Z] is simply referring to all letters both lowercase and uppercase. (They could have also just used \w which means the same thing as [a-zA-Z]). The below is checking that the attribute, legacy, is a string containing only letters.

https://guides.rubyonrails.org/v5.1/active_record_validations.html

numericality- This method checks that the given attribute has only numeric values. If you’d like to further specify only integral numbers, you would then follow numericality with :only_integer: true. In the below example the validation is checking that the points attribute is a number (integer or float). The next validation is checking that the games_played attribute is specifically an integer number.

https://guides.rubyonrails.org/v5.1/active_record_validations.html

In place of :only_integer in the numericality validation, there are other built in constraints that can be used as well. See the below.

https://guides.rubyonrails.org/v5.1/active_record_validations.html

Some Examples from our ActiveRecord Validations Labs:

https://learn.co/tracks/web-development-immersive-2-0-module-two/rails/validations-and-forms/activerecord-validations-lab

The presence and length validations above are pretty self explanatory, but let’s review the inclusion and format validations used. The requirement was that the category had to be either Fiction or Non Fiction. Here we can pass in the category attribute and use the inclusion validation to pass in “%w(Fiction Non-Fiction)”. This will ensure that for category to be valid, the value has to be either Fiction or Non-Fiction. The next requirement was that the title had to contain “Won’t Believe”, “Secret”, “Top [number]”, or “Guess” otherwise it wouldn’t be valid. Here we can use the format validation which uses regular expressions. This validation is checking that the value matches those given titles. The “\d” is a regex that simply refers to any digit [0–9].

Above, is an example from another lab where we had some slightly more complicated validations. We had to ensure that the title was not blank and that it cannot be repeated by the same artist in the same year. We first checked that the title is unique with the presence validation. Secondly we can use the uniqueness validation to ensure the title is not repeated, but the uniqueness method also allows us to pass in scope. Within the scope, the attribute release_year should be passed in. This ensures that within the same release year, the title cannot be repeated.

Our last validation requirement was that the release year has to be an integer, is optional if released is false, must not be blank if released is true and must be less than or equal to the current year. Here we needed to use presence, numericality and a custom if statement. The released? method is simply checking if the released attribute is true. So in our validation, our released? method will check if the released attribute is true, and if so it will use presence to ensure that the release year is not blank. Then to make sure the release year is a number, we can use the numericality method and lastly to verify that the release year is is less than the current year.

Errors & Displaying Errors on the View Page

In order to display error messages on an object, you can use the method called errors. If there are errors on an object, calling .errors.messages will return an array of strings with all the errors on that object. Below, after instantiating a person instance without a name attribute, calling .errors.messages returns an array of the error messages. If you’d like to return the error messages in a more user friendly format, you can call error.full_messages. Doing so will prepend the capitalized attribute to each error message. You can also call .errors on a specific attribute like person.errors[:name], which will then return the errors (if any) associated with that particular attribute.

https://guides.rubyonrails.org/v5.1/active_record_validations.html#working-with-validation-errors
https://guides.rubyonrails.org/v5.1/active_record_validations.html#working-with-validation-errors

In the create method of the controller, you should first create an instance, and then check to see if that instance is valid. If it is valid, it should redirect to the show page for that instance, otherwise you can either render:new or redirect to the new page path. When redirecting to the new page it is best to utilize the flash method to display the errors. When rendering to the new page, it is best to use the if errors.any? method. In the new view page is where you will be displaying your errors to the user(if there are any).

Below are some examples:

Create Method using redirect to new and flash errors:

Controller-Create method: using redirect : Credit to Evans’ lecture code

New View Page using flash errors:

View Page-form: Credit to Evans’ lecture code

Create Method using render to new:

Controller-Create method: using render : crud-with-validations-lab

New View Page using .errors.any?:

View Page-form render: crud-with-validations-lab