Models

Model classes inherits from the rails ApplicationRecord class.

Here is an example of the ApplicationRecord class for one of our application:

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true

  connects_to database: { 
    writing: :database_name, 
    reading: :database_name 
  }
end

Model classes are the interface between our application and our database.

Naming conventions

As always, Rails favour conventions over configurations. By default, ActiveRecord uses some naming conventions to find out how the mapping between model classes and database tables should be done.

Model classes are singular and capitalized, and they follow the CamelCase convention. In the other hand, database tables are plural with underscores separating words (following the snake_case convention).

Here are a few examples of model classes and their database tables:

Model

File Name

Table

User

user.rb

users

Company

company.rb

companies

UserLine

user_line.rb

user_lines

Creating models

To create a new model, you can simply add a new file under the models folder. Just be careful, there has to be a corresponding table in the database in order to use it. This means that you have to run a migration in order to create that table in the database.

If you're lazy, you can simply run:

rails g model User name:string email:string company:references

This command will create a migration file that can be run to create the table in the database, and it will also create the model class with the appropriate attributes. Just don't forget to run the migration using rails db:migrate to create the table and to make the model usable.

In this example, we can see that it will create a table with two string attributes (name & email), but it will also add another attribute: company. This one is a bit different from the previous ones, because we don't directly assign a type to it. Instead, we say that the company attribute is a reference to a record in another table. This means that we already have a table called companies and the equivalent model class. With the naming of the attribute, which correspond to an existing model, Rails will be able to find the right table it refers to. Convention over configuration they say.

In case you want to see more of rails generate command lines, check out this cool cheat sheet.

Example

Here is an example of a model class:

class User < ApplicationRecord
  belongs_to :company

  scope :for_company, ->(company) { where(company: company) }

  delegate :name, to: :company, prefix: true

  def name_or_email
    [first_name, last_name].compact.join(' ').blank? ? email : [first_name, last_name].compact.join(' ')
  end
end

Attributes

We can see that its attribute are not defined here, but you can still simply access them by calling them on your record. For example, if you've retrieved a user record, you can call user.first_name. So keep in mind that every table column is accessible, even if they are not explicitly defined on the model class.

Relations

On the other hand, the relations must be explicit in your model class. In this example, it's on the second line: belongs_to :company. This means that we have an attribute called company which references another record in our database.

If you used rails g modelto generate your model , your relations should already be present in your model class.

Scope

You can also find a scope on line 4. This scope will allow you to reduce your queries by scoping them the way you want. In this case, calling User.for_company(4) will retrieve every users related to the company whose ID is 4. They are method aliases for where clauses.

Delegate

On the 6th line, you can see that we added an attribute by delegation. This means that we will delegate this attribute to the company (an instance of another class), and give an access to it on the user record with the company prefix. In other words, we can directly do user.company_name instead of doing user.company.name.

Custom methods

Finally, you can see on lines 8 to 10 that we defined a custom method for this model class. This method will return the first_name and the last_name if they are present, or return the email otherwise. We can directly use it on our record like this: user.name_or_email.

Reading & Writing data

ActiveRecord follows the CRUD implementation, which is an acronym for Create, Read, Update & Delete. By default, ActiveRecord defines multiple methods that you can directly call on your model classes.

Create

To create a new instance of a model class, we can think of two methods: new and create. The first one will return a new instance of your model class, while the second one will do the same but will also persist the newly created instance to the database.

Let's take a deeper look with our User model class:

# Returns the new user
user = User.new(first_name: 'Quentin', last_name: 'Tarantino')

# Returns the new user after having try to save it in the database
user = User.create(first_name: 'Quentin', last_name: 'Tarantino') 

Another set of methods we can frequently find in our applications are find_or_initialize_by and find_or_create_by. Their names are quite explicit: they will either find or create a new record. The difference between these two is the same than the one for new and create. The first one will return a record, the second one will return a record and save it to the database.

# This should find the existing record in the DB and return it
user = User.find_or_initialize_by(last_name: 'Tarantino')

# This should initialize the new record in the DB and return it
user = User.find_or_initialize_by(last_name: 'Kubrick')

Read

Reading data from the database is super easy thanks to ActiveRecord. It provides a rich set of methods to access data within a database. Below are a few examples of different data access, and if you want more information you can find it on this guide.

# Retrieve the user with the ID 18
user = User.find(18)

# Retrieve the first user
user = User.first

# Retrieve the first user whose name is Tarantino
user = User.find_by(last_name: 'Tarantino') 

# Retrieve an array of users who were invited
users = User.where(invited: true)

Also, you can now use your awesome scope:

# Retrive an of array of users whose company has the ID 4
users = User.for_company(4)

Update

Once a model instance has been retrieved, its attributes can be directly modified and then saved to the database.

You can do user.name = 'Steven Spielberg' and then user.save to save the modifications into the database. You can also directly do user.update(name: 'Steven Spielberg') which will take care of both the modification and the saving.

You can also update multiple attributes at once using update_attributes or assign_attributes. The only difference between those two is that the first one will update and save, while the second one will just update without saving (you'll still have to do it if you want your modifications to be persisted). Both of these methods need a hash as parameter which contains the attributes you want to edit. Check out the example below:

user.update_attributes({
    first_name: 'Steven', 
    last_name: 'Spielberg',
    email: 'steve@etproductions.com'
})

Delete

As for the update, any record can be deleted once it has been retrieved. To do this, you simply have to call the method destroy on the record you want to delete.

You can also destroy every record of an array by call the destroy_all method on the collection representing your records. You have to be very careful with that though, as it is quite definitive.

# Destroy a specific record
user.destroy 

# Destroy every users whose company ID is 4
users = User.for_company(4)
users.detroy_all

Nested attributes

Rails allows you to save nested attributes through the parent record.

By default, this option is turned off, but you can enable it by using the accepts_nested_attributes_for method.

Let's analyze this with an example. Here is our (simplified) company model:

class Company < ApplicationRecord
  has_many :users, dependent: :destroy

  accepts_nested_attributes_for :users
end

If you have retrieved a company object, and you want to update all of its users, you can simply do company.users = all_users(considering all_users to be an array of user records).

In our Prospect.io application, we use a monkey patch that supplement the rails NestedAttributesmodule to support more options. You can find this file called nested_attributes.rbin our codebase.

On the second line, you can see that we declared the users relation to the company (as we did in the user model for the company). What is interesting here is the dependent: destroy. This means that if we delete a company, all of its users will be destroyed as well. To be more precise, the method destroy will be called on every user records of this company. This is how to delete nested records when the parent record is being deleted.

Last updated