Model Validations For Rails
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.
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: :destroyvalidates :username, :password, :bio, presence: true
validates :username, uniqueness: trueend
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: :destroyvalidates :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: :destroyvalidates :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: :destroyvalidates :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)
endend
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