An association is a connection between two Active Record models. It makes much easier to perform various operations on the records in your code. We will divide associations into four categories:
- One to One
- One to Many
- Many to Many
- Polymorphic One to Many
If you are new to Ruby on Rails, be sure to get up your rails project ready and check how to create models in Rails.
One to One
When you set up one-to-one relations you are saying that a record contains exactly one instance of another model. For example, if each user in your application has only one profile, you can describe your models as:
class User < ApplicationRecord class Profile < ApplicationRecord
has_one :profile belongs_to :user
end end
One of the models in the given relation will have has_one
method invocation and another one will have belongs_to
. It is used to describe which model contains a foreign key reference to the other one, in our case, it is the profiles model.
Cover your associations with RSpec tests to make sure everything works the way you want it to.
One to Many
A one-to-many association is the most common used relation. This association indicates that each instance of the model A can have zero or more instances of another model B and model B belongs to only one model A.
Let's check it out via example. We want to create an application where users can write multiple stories, our models should look like this:
class User < ApplicationRecord class Story < ApplicationRecord
has_many :stories belongs_to :user
end end
Here, deciding which model will have has_many
and which will have belongs_to
is more important than in one-to-one relations, because it changes the logic of your application. In both cases, the second model contains one reference to the first model in form of a foreign key.
The second model doesn't know about the first model relation to it - it is not aware if the first model has a reference to more than one of them or just to one.
The tests for associations get more complex to write the more relations you create. You should check it out how to create your own association factories to make your testing life easier.
Many to Many
Many-to-many associations are a bit more complex and can be handled in two ways, "has and belongs to many" and "has many through" relations.
Has and Belongs to Many
A has_and_belongs_to_many
association creates a direct many-to-many connection with another model. It is simpler than the other one, as it only requires calling has_and_belongs_to_many
from both models.
Example: Let's say, a user can have many different roles and the same role may contain many users, our models would be like this:
class User < ApplicationRecord class Role < ApplicationRecord
has_and_belongs_to_many :roles has_and_belongs_to_many :users
end end
You will need to create a join table for this association to work. It is a table that connects two different models. The join table is created with rails function create_join_table :user, :role
in a separate migration.
class CreateUserRoles < ActiveRecord::Migration
def change
create_table :user_roles, id: false do |t|
t.references :user, index: true, foreign_key: true
t.references :role, index: true, foreign_key: true
end
end
end
This is a very simple approach, but you don't have the direct access to related objects, you can only hold references to two models and nothing else.
Has Many Through
Another way to define a many-to-many association is to use the has many through association type. Here we define a separate model, to handle that connection between two different ones.
Instead of putting down a new example, you should check this one out! It explains everything you should know about this association.
Using the example of has_and_belongs_to_many
association, this time the three models should be written like this:
class User < ApplicationRecord
has_many :user_roles
has_many :roles, through: :user_roles
end
class UserRoles < ApplicationRecord
belongs_to :user
belongs_to :role
end
class Role < ApplicationRecord
has_many :user_roles
has_many :users, through: :user_roles
end
This association will enable you to do things like user.role
and to get a list of all connected second model instances. It will also enable you to get access to data specific to the relation between first and second models.
Polymorphic
Polymorphic associations are the most advanced associations available to us. You can use it when you have a model that may belong to many different models on a single association.
Let's imagine you want to be able to write comments for users and stories. You want both models to be commentable. Here's how this could be declared:
class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: true
end
class Employee < ApplicationRecord
has_many :comment, as: :commentable
end
class Product < ApplicationRecord
has_many :comment, as: :commentable
end
You can think of a polymorphic belongs_to
declaration as setting up an interface that any other model can use. To declare the polymorphic interface you need to declare both a foreign key column and a type column in the model. You should run the migration once you are done.
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.text :body
t.integer :commentable_id
t.string :commentable_type
t.timestamps
end
add_index :comments, :commentable_id
end
end
This migration can be simplified by using references:
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.text :body
t.references :commentable, polymorphic: true, index: true
t.timestamps
end
end
end
Aditional Options
There are a few additional options you can use when defining relations between models, we will cover two most commonly used ones.
class_name
If the name of the other model cannot be derived from the association name, you can use the
:class_name
option to supply the model name. You can read more about this on RailsGuides.Example: You want the belongs_to relation to call for an author, but there are two problems:
- There is no model called in that way.
- The story table is missing an
author_id
field.
Easy fix! To make this work and stop your tests from failing, you just have to specify the :foreign_key
and the actual name of the class:
class Story < ApplicationRecord
belongs_to :author, class_name: 'User', foreign_key: 'user_id'
end
dependent
You can use this option when you want to get rid of orphaned records since they can lead to various problems. Orphaned records are created when we delete or destroy a model A that was associated with model B, but model B wasn't removed in the process.
class User < ApplicationRecord
has_many :stories, dependent: :destroy
end
Example: Suppose, a user called Maria wrote a lot of stories. Then, we deleted Maria from the database, but the stories still have the user_id column set to Maria's id. These stories are called orphaned records.
Thank you for reading!