Let's take the simple example of a blog with:
- A post
habtmtags - A tag
habtmposts
Let's assume that a Post and Tag model already exist. This is the migration for the join table
class CreatePostsTags < ActiveRecord::Migration[5.1]
def change
create_table :posts_tags, id: false do |t|
t.belongs_to :post, index: true
t.belongs_to :tag, index: true
end
end
endAnd the relations in each model
class Post < ApplicationRecord
has_and_belongs_to_many :tags
endclass Tag < ApplicationRecord
has_and_belongs_to_many :posts
endCool! it works !
Few months later, let's assume that you want to order tags in each post for searching purposes.
In our case, has_many :through permits to declare the join table as model. And so, in order to operate (filter, sort, allow, reject, etc..) at a join table level.
That permits to store the rank of the tag in the post's tag list direclty in the join model.
class CreatePostsTags < ActiveRecord::Migration[5.1]
def change
create_table :posts_tags, id: false do |t|
t.belongs_to :post, index: true
t.belongs_to :tag, index: true
end
end
endA model needs a primary key to be retrieved. By convention rails uses the id column as primary key. The problem is that we've removed the id column from our join table (id: false). this makes sense because we just need the post_id and tag_id to retrieve a record.
But in order to use has_many :through, we need to add a primary key to the posts_tags table. So how to do so ?
class AddPrimaryKeyAndRankToPostsTags < ActiveRecord::Migration[5.1]
def change
rename_table 'posts_tags', 'post_tags'
add_column :post_tags, :id, :primary_key
add_column :post_tags, :rank, :integer, default: 0
end
endThen in models
class PostTag < ApplicationRecord
belongs_to :post
belongs_to :tag
endclass Post < ApplicationRecord
has_many :post_tags, -> { order(rank: :asc) }
has_many :tags, through: :post_tags
endclass Tag < ApplicationRecord
has_many :post_tags
has_many :posts, through: :post_tags
endVoilà !


Thank you for the kind words!
Hope that helps!!