DEV Community

Nesha Zoric
Nesha Zoric

Posted on

Rails Associations Everything There Is to Know About Associations in Rails

An association is a connection between two Active Record models. It makes much easier to perform various operations on the records in your code. I divided associations into four categories:

  1. One to One
  2. One to Many
  3. Many to Many
  4. 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 your case, it is the profiles model.

one-to-one

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. Let’s say you want to create an application where users can write multiple stories, your 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.

one-to-many

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.

has-and-belongs-to-many

Example: Let's say, a user can have many different roles and the same role may contain many users, your 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 you should define a separate model, to handle that connection between two different ones.

has_many_through

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.

polymorphic

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, I 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:

  1. There is no model called in that way.
  2. 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 you 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, you 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!

This article is originally published on Kolosek Blog.

Top comments (8)

Collapse
 
thorstenhirsch profile image
Thorsten Hirsch

I think the habtm table should be called 'users_roles' (note the plural for both models) instead of 'user_roles'. See stackoverflow.com/questions/656133....

Collapse
 
neshaz profile image
Nesha Zoric

Thank you for noticing! You were right, the correct naming convention in habtm is to use plural for the associated models.

Collapse
 
samuelna profile image
Samuel • Edited

Small typos for polymorphic association example code.

...

class Employee < ApplicationRecord
  # should be comments
  has_many :comment, as: :commentable
end

class Product < ApplicationRecord
  # should be comments
  has_many :comment, as: :commentable
end
Enter fullscreen mode Exit fullscreen mode
Collapse
 
agorneo profile image
Andreas P. Gortsilas

Dear Nesha Zoric hello :)
A wonderful new day for all of us :)

I would like to thank you for this wonderful article and the amazing work that you have done in this.
I am grateful to you

With my warmest regards for all the world

Collapse
 
paulofrodriguez profile image
paulofrodriguez

Lil' confusing for a noobie. You said you should use create_join_table but you use create_table. And does rails know he needs to fillout the ids on the join table automagically ?

Collapse
 
ed3899 profile image
Edward Casanova

The arrows in the diagram are reversed.

Here are some correct examples:
datensen.com/blog/er-diagram/one-t...

Collapse
 
sevicode profile image
Sevicode

Great explanation of associations. Thanks Nesha

Collapse
 
webbydevvy profile image
Charlie Strack

Great explanation!