Model Validations For Rails

Dan Romans
7 min readMay 1, 2020

--

Chris Farley and David Spade in the film Tommy Boy, performing a botched sales pitch.
“Do you validate?”

Rails is a really great framework with many built in features. Since one of the principal strengths of Rails is providing a structure on which to build a database, Rails offers an incredibly useful gem called Active Record. Active Record handles the M—model—of the MVC paradigm, and is an ORM system that allows objects to be mapped to a database. This means Ruby classes can be used to automagically perform SQL queries and manage the database. In layperson’s terms, Active Record allows translation and conversion so that Ruby can be used to simply perform more complex tasks within Rails.

When using a database, there needs to be checks that the data being added, removed, or altered is copacetic, and never a threat to the security or functionality of the database. Active Record includes validation features so data offered to the database can be validated before being accepted and persisted. Since Ruby and Rails are often first learnings for developing technologists (no pun), I wanted to present an overview of Active Record validations for those taking the next step in developing their first Rails applications.

For the following example, let’s imagine you have a Rails application or Rails API set up, scaffolded, the whole shebang. You have a user class with the following attributes: username, password, bio. There’s also a posts class, and a user can have many posts. Your user class may look like this:

# user.rbclass User < ApplicationRecord
has_many :posts, dependent: :destroy
end

So there’s a class User which inherits from ApplicationRecord and has a has_many relationship to :posts. Notice that dependent: :destroy is assigned to the has_many relationship, signifying that all posts belonging to a user should be deleted if that user is deleted. Also, note that no getter or setter, e.g. attr_accessor, is specified for the user attributes since they are mapped to the database table.

Imagine a real world scenario being a form submission from the client side. What would happen if the user didn’t include a bio, or submitted a password that was one character in length? There’s a need to control the data our application is using, for organization, or, at the most extreme, protection from HACKERMAN.

Hackerman from the film Kung Fury.
Yes, HACKERMAN was hacking with a just purpose, but, still…

The client side of an application can validate form data, but there needs to be an insurance on the server side, which, ideally, is inaccessible. This is where Active Record validations come in. Validations can be used in different ways, but a great place to start is with the even more helpful validation helpers that Active Record provides. These helpers are established to provide common use validations like whether a required field was entered or an entry matches the required length of characters. Let’s add some validations for our user attributes. We’ll start with the presence validation, which checks that the required field is not blank.

# user.rbclass User < ApplicationRecord
has_many :posts, dependent: :destroy
validates :username, :password, :bio, presence: trueend

How easy is that?! The key word validates precedes the attributes to be validated (separated by commas), and is succeeded by the presence validator, assigned the condition true. The presence validator checks that the attribute is not nil or an empty string.

Another helpful validation helper is the uniqueness validator, which checks to make sure the database does not already contain the submitted value. A great application for this is to make sure only one user can have a specific username. For example:

# user.rbclass User < ApplicationRecord
has_many :posts, dependent: :destroy
validates :username, :password, :bio, presence: true
validates :username, uniqueness: true
end

We see the same pattern as with the presence validator. These validations provide default error messages to the client side, which we’ll come back to in a bit, but let’s say that we wanted to customize the error message sent to the client side in the case of our uniqueness validator. It can be done by adding a message option in place of the condition true, and dynamically interpolating the value being validated with percent sign notation.

# user.rbclass User < ApplicationRecord
has_many :posts, dependent: :destroy
validates :username, :password, :bio, presence: true
validates :username, uniqueness:
{message: 'An account associated with %{value} already exists'}
end

Say a user wanted to create an account with the username tommyboy95, but the username they submitted already exists in the database. The custom error message would be returned as such:

username An account associated with tommyboy95 already exists

Pretty useful stuff. Another common validation example is to check the length of attributes. Suppose we wanted to make sure that a password is between 5 and 15 characters and a bio is no more than 2000 characters. We can add length validations like so:

# user.rbclass User < ApplicationRecord
has_many :posts, dependent: :destroy
validates :username, :password, :bio, presence: true
validates :username, uniqueness:
{message: ': An account associated with %{value} already exists'}
validates :password, length: {in: 5..15}
validates :bio, length: {maximum: 2000}
end

Another specification that can be made is which controller actions these validations should be performed on. Say, for example, we want to validate the password when it is created for the first time, but not allow passwords to be changed once created. We can specify this like so:

# user.rbclass User < ApplicationRecord
has_many :posts, dependent: :destroy
validates :username, :password, :bio, presence: true
validates :username, uniqueness:
{message: 'An account associated with %{value} already exists'}
validates :password, length: {in: 5..15}, on: :create
validates :bio, length: {maximum: 2000}
end

There’s a lot more that can be done, including creating custom validations. Check out the documentation to see all that is possible. Now that you have a sense of how to establish validations in Ruby classes, how can the validations be performed?, and how can error messages be accessed and used in the case a validation fails? Let’s first address how validations can be performed.

Enter the C—controller—of the MVC paradigm. Within the controller actions of your application you can use the valid? method (alternatively, the invalid? method), which act as booleans, to check the validity of data to be saved to the database. Keep in mind that the valid? and invalid? methods only verify that data could or could not be persisted to the database. This means they must be used to validate a value after the create method or the save method is used on an object, not the new method.

Let’s write an example of the create action for the users controller. The example will include a private user_params method to streamline the user attributes submitted with forms from the client side, the valid? method, and the render method to prepare content for the browser. In this case, we’ll render responses with JSON and an HTTP status, as though the client side of the application was built with, for example, React.

# users_controller.rbclass UsersController < ApplicationController  def create
user = User.create(user_params)
if user.valid?
render json: {user: user}, status: :created
else
render json: {errors: user.errors.full_messages},
status: :not_acceptable
end
end
private def user_params
params.permit(:username, :password, :bio)
end
end

Within the create action, we create a user object with user_params and assign it to the variable user. The valid? method is called on user. If user successfully persists to the database, a JSON response including an object for the user and a created status is transmitted. If not, we know that at least one model validation has failed, and a different kind of object is included in the JSON response, including a not acceptable status. We see an object with a key of errors, and a value of user.errors.full_messages. Seems convenient. Let’s touch on how this is generated.

Active Record works with validations to provide a modified hash (i.e. object) that contains all attribute errors for an object. There are numerous methods that can be paired with this automatically generated errors object to pass useful information to the client side. In the example above, if the newly created user object fails validations, the full_messages method is called on the errors object of the user object, which will provide an Array of error messages that read like English. Pretty nifty stuff, and there’s much more!

Let’s see how some console.log output in the hypothetical React frontend will differ based on validations.

*Note: abbreviated for demonstration purposes, and…
NEVER STORE UNSECURED PASSWORDS IN A DATABASE!

User.create(
username: 'tommyboy95',
password: 'holyschnikes',
bio: 'College grad, heir to Callahan Auto'
)
# =>
{
id: "1",
user: {
username: “tommyboy95”,
password: “holyschnikes”,
bio: “College grad, heir to Callahan Auto”
},
status: 201(Created),
type: “user”
}
####################################################################User.create(
username: 'tommyboy95',
password: 'tom'
)
# =>
{
errors: [
"Bio must exist",
"Password is too short (minimum is 5 characters)",
"Username An account associated with tommyboy95 already exists"
],
status: 406 (Not Acceptable)
}

I hope this was helpful if you’re just getting started with securing Rails applications. I feel that, once someone shows you the ropes, it’s a bit easier to master the complexities of a tool or task, and further your own understanding. I hope this article served as a worthy introduction to the concept of using Active Record validations with Ruby on Rails.

github.com/dangrammer
linked.com/in/danieljromans
danromans.com

--

--

Dan Romans

// fullStackWebDeveloper, # software_engineer, Musician & Woodworker