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.
There are three main association types.
One-to-One Association: One object is related with one and only object.
Example: If there are two entitiesPerson(Id, Name, Age, Address)
andPassport(Passport_id, Passport_no)
. So, each person can have only one passport and each passport belongs to only one person.
One-to-Many Association: One object has many objects and many objects belongs to the One object.
Example: If there are two entity typeCustomer
andAccount
then eachCustomer
can have more than oneAccount
but eachAccount
is held by only oneCustomer
. 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.
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 typeCustomer
andProduct
then each customer can buy more than one product and a product can be bought by many different customers.
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
class Passport < ApplicationRecord
belongs_to :person, optional: true # does not validates the presence of the person object
end
has_one
Association
A 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
has_many
Association
A has_many
association 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
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
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
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
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
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
: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
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})
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)