DEV Community

Cover image for Ruby on Rails ActiveRecord Associations
Eapen Zacharias
Eapen Zacharias

Posted on

Ruby on Rails ActiveRecord Associations

Much of the power of Ruby on Rails lies in creating good associations to keep our data well organized, so that it can be used efficiently.
Association Symbols

There are three main association types.

  • One-to-One Association: One object is related with one and only object.
    Example: If there are two entities Person(Id, Name, Age, Address) and Passport(Passport_id, Passport_no). So, each person can have only one passport and each passport belongs to only one person.
    One-to-One Association

  • One-to-Many Association: One object has many objects and many objects belongs to the One object.
    Example: If there are two entity type Customer and Account then each Customer can have more than one Account but each Account is held by only one Customer. In this example, we can say that each Customer is associated with many Account. So, it is a one-to-many relationship. But, if we see it the other way i.e many Account is associated with one Customer then we can say that it is a many-to-one relationship.
    One-to-Many Association

  • Many-to-Many Association: Each object of the first class can be related to one or more than one object of the second class and a single object of the second class can be related to one or more than one object of the first class.
    NOTE: For using Many-to-Many association we need a joint table as we can’t include many foreign keys to a table. A joint table serves as a way to make links between two different tables. The joint table will have foreign keys of each pair of related objects from the both tables.
    Example: If there are two entity type Customer and Product then each customer can buy more than one product and a product can be bought by many different customers.
    Many-to-Many Association

Association Method

  • One-to-One relationships: has_one, belongs_to, has_one :through
  • One-to-Many relationships: has_many, belongs_to
  • Many-to-Many relationships: has_and_belongs_to_many, has_many :through

belongs_to Association

A belongs_to association sets up a connection with another model, and declares model "belongs to" one instance of the other model.

Note: When you define belongs_to inside a model, the other object that that belongs_to is not optional. If it belongs to it, then that other object has to be assigned. This happens because Rails adds a validation that check if the referenced object is present. Please also note that order versions of Rails do not do that.
You can disable this validation by passing {:optional => true}

For example, if your application includes person and passport, and each passport can be assigned to exactly one person, you'd declare the passport model this way:

class Passport < ApplicationRecord
  belongs_to :person # this validates the presence of the person object
end
Enter fullscreen mode Exit fullscreen mode
class Passport < ApplicationRecord
  belongs_to :person, optional: true # does not validates the presence of the person object
end
Enter fullscreen mode Exit fullscreen mode

has_one Association

has_one association shows that another model is referenced to this model. That model can be fetched through this association.

For example, if each person in your application has only one passport, you'd declare the person model like this:

class Person < ApplicationRecord
  has_one :passport
end
Enter fullscreen mode Exit fullscreen mode

has_many Association

has_manyassociation indicates a one-to-many connection with another model and often used with belongs_to on the other model. This association declares that each instance of the model has zero or more instances of another model.

For example, in an application containing customers and accounts, the customers model could be declared like this:

class Customer < ApplicationRecord
  has_many :accounts
end
Enter fullscreen mode Exit fullscreen mode

has_many :through Association

A has_many :through association is used to set up a many-to-many connection with another model. This declares a model can be matched with zero or more instances of another model through a third model.
For example, consider a online shop where customers make purchases to get products. The association declarations could look like this:

class Customer < ApplicationRecord
  has_many :purchases
  has_many :products, through: :purchases
end

class Purchase < ApplicationRecord
  belongs_to :customer
  belongs_to :product
end

class Product < ApplicationRecord
  has_many :purchases
  has_many :customers, through: :purchases
end
Enter fullscreen mode Exit fullscreen mode

has_one :through Association

A has_one :through association sets up a one-to-one connection with another model through a third model. This declares the model can be matched with one instance of another model through a third model.

For example, if each person has one passport, and each passport is associated with one passport_issue, then the models could look like this:

class Person < ApplicationRecord
  has_one :passport
  has_one :passport_issue, through: :passport
end

class Passport < ApplicationRecord
  belongs_to :person
  has_one :passport_issue
end

class PassportIssue < ApplicationRecord
  belongs_to :passport
end
Enter fullscreen mode Exit fullscreen mode

has_and_belongs_to_many Association

A has_and_belongs_to_many association creates a direct many-to-many connection with another model, without the help of a third model and declares this model can be matched with zero or more instances of another model

For example, if your application includes assemblies and parts, with each assembly having many parts and each part appearing in many assemblies, you could declare the models this way:

class Assembly < ApplicationRecord
  has_and_belongs_to_many :parts
end

class Part < ApplicationRecord
  has_and_belongs_to_many :assemblies
end
Enter fullscreen mode Exit fullscreen mode

Choosing Between has_many :through and has_and_belongs_to_many: The simplest rule of thumb is that you should set up a has_many :through relationship if you need to work with the relationship model as an independent entity. If you don't need to do anything with the relationship model, it may be simpler to set up a has_and_belongs_to_many relationship (though you'll need to remember to create the joining table in the database).

Polymorphic Associations

A polymorphic association allows a model to belong to multiple other models. In this case, we might create a Profile model and a Blog model, and both have many references to Images.

class Image < ApplicationRecord
  belongs_to :imageable, polymorphic: true
end

class Profile < ApplicationRecord
  has_many :images, as: :imageable
end

class Blog < ApplicationRecord
  has_many :images, as: :imageable
end
Enter fullscreen mode Exit fullscreen mode

We can consider polymorphic belongs_to declares that any models can set up an interface with model and use it. In this case we can retrieve an image from a profile instance using @profile.image and a collection of images from a blog instance using @blog.images

Additional Features in ActiveRecords

:dependent => :destroy

When the object is destroyed, destroy will be called on its associated objects.

Example: In this case when a user is deleted all the comments created by the user also gets destroyed

class User < ApplicationRecord
    has_many :comments, dependent: :destroy
end

class Comment < ApplicationRecord
    belongs_to :user
end
Enter fullscreen mode Exit fullscreen mode

:dependent => :delete_all

The difference here is that :delete_all, like the delete_all method, is skipping the callbacks that are in that comment object. It just goes straight to the database, and writes SQL to delete them. Destroy is a safer choice, because comment may have callbacks as well.

class User < ApplicationRecord
    has_many :comments, dependent: :delete_all
end

class Comment < ApplicationRecord
    belongs_to :user
end
Enter fullscreen mode Exit fullscreen mode

Joining Tables

Once we have associations between our models, we can start to work with them in interesting ways. And one of those ways is by joining tables during our queries. If you're familiar with working with databases, you'll know that there are times when we want to be able to join two different tables together during a query. So we can query columns on more than one table.

We can use SQL joins to query columns on several tables.

Example: The given code will return Products for which its category is active

Product.joins(:category).where('categories.active = 1')
# or you can use a hash format
Product.joins(:category).where(categories: {active: true})
Enter fullscreen mode Exit fullscreen mode

Here are the few ways we can join a table:

  • Using a String SQL Fragment: We can provide the raw SQL specifying the JOIN clause Author.joins("INNER JOIN books ON books.author_id = authors.id AND books.out_of_print = FALSE")
  • Joining a Single Association: Product.joins(:category)
  • Joining Multiple Associations: Product.joins(:category, :inventory)

By joining tables together with the joins method, you have the ability to start working with your associations in interesting ways.

Thanks For Reading, Follow Me For More
Share your suggestions, doubts and feedback in the comments section!

Top comments (0)