Multiple Associations Between 2 models in Rails
by Gaurav Koley
I have been working on a project as a part of Web Science Lab @ IIIT-B which is essentially an online marketplace of content. I am building this marketplace using Ruby on Rails 5 for backend and VueJS for a rich frontend.
The marketplace has Resources and Users. There are other models in the platform as well but for the sake of this post, Resources and Users are the models of interest.
Users can create some resources and consume some other resources. This entails that Users may be either Creators or Consumers or both. Likewise, the Resource can be termed as Creation or Consumable respectively. This presents a somewhat rare situation. There, now exist, 2 associations between the same 2 models.
A Resource might have multiple creators and a Creator might create multiple resources. Also, it might be obvious to notice that a Resource will have multiple Consumers and a Consumers will consumer multiple resources. This can be depicted as in this image:
To achieve this, what I did was have a join model Association generated using:
→ rails g scaffold association user:belongs_to resource:belongs_to association_type:string
Here association_type helps us achieve the multiple associations. In the application, we restrict it to have only one of the two specified values: consumer or creator. This is done by:
# app/models/association.rb
class Association < ApplicationRecord
belongs_to :user
belongs_to :resource
validates :association_type, presence: true, inclusion: { in: %w(consumer creator),
message: "Association %{value} is not one of the types available."
}
end
Now, we setup the User and Resource Models as following:
# app/models/user.rb
class User < ApplicationRecord
has_many :associations
has_many :creation_associations, -> {where 'association_type = "creator"'}, class_name: "Association"
has_many :consumable_associations, -> {where 'association_type = "consumer"'}, class_name: "Association"
has_many :creations, through: :creation_associations, source: :resource
has_many :consumables, through: :consumable_associations, source: :resource
end
# app/models/resource.rb
class Resource < ApplicationRecord
has_many :associations
has_many :creators, -> {where 'association_type = "creator"'}, class_name: "User", through: :associations
has_many :consumers, -> {where 'association_type = "consumer"'}, class_name: "User", through: :associations
end
Now, a Creator(User) can access their creations as: user.creations
and a Consumer can access their consumables
through user.consumables
. Likewise, resources have handles:
resource.creators # For list of creators of this resource
resource.consumers # For a list of Consumers
With this setup, multiple types of associations between 2 models might be established. This setup is extendable to include other kinds of associations (maybe, maintainer or licencer etc).
Hope this helped. For any queries, ping me at my email: gaurav__at__koley__dot__in