DEV Community

Kartikey Tanna
Kartikey Tanna

Posted on • Originally published at kartikey.dev on

Many-to-many self join in Rails

Let’s say we have “products” and we want to prepare “kits” of those products. Kits are nothing but the group of the products.

We can use many-to-many relationship here because products can be in many kits, and kits can be associated with many products.

Also, since the kits are just grouping the products, we can use self-joins. There are multiple ways we can implement self-joins.

Using has_and_belongs_to_many

You can read more about has_and_belongs_to_many on Rails docs.

Migration

class CreateJoinTableProductKits < ActiveRecord::Migration[6.0] def change create_table :product_kits, id: false do |t| t.references :product, null: false, foreign_key: true, index: false t.references :kit, null: false, foreign_key: { to_table: :products }, index: false t.index [:product_id, :kit_id], unique: true end end end 
Enter fullscreen mode Exit fullscreen mode

Model

class Product < ApplicationRecord has_and_belongs_to_many :kits, join_table: :product_kits, class_name: 'Product', association_foreign_key: 'kit_id' end 
Enter fullscreen mode Exit fullscreen mode

Using has_many through

This approach is better because later on in your project you can add more fields and validations in ProductKit model.As you know, our projects are always dynamic and most of the time(all the time) we end up modifying the flow. So, it isbetter to be prepared and use has_many :through from the beginning.

More on, has_many :through on Rails docs.

Migration

class CreateJoinTableProductKits < ActiveRecord::Migration[6.0] def change create_table :product_kits do |t| t.references :product, null: false, foreign_key: true, index: false t.references :kit, null: false, foreign_key: { to_table: :products }, index: false t.index [:product_id, :kit_id], unique: true t.timestamps end end end 
Enter fullscreen mode Exit fullscreen mode

Model app/models/product.rb

class Product < ApplicationRecord has_many :product_kits, dependent: :destroy has_many :kits, through: :product_kits end 
Enter fullscreen mode Exit fullscreen mode

Model app/models/product_kit.rb

class ProductKit < ApplicationRecord belongs_to :product belongs_to :kit, class_name: 'Product' end 
Enter fullscreen mode Exit fullscreen mode

Top comments (2)

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
usman1436 profile image
Usman1436

Wow, really helpful.
Thanks