Reputation: 8055
I have two models StageBatch
and StageItem
:
class StageItem < ApplicationRecord
has_and_belongs_to_many :stage_batches
end
class StageBatch < ApplicationRecord
has_and_belongs_to_many :stage_items
end
With the following migration:
class CreateStageBatchesAndStageItems < ActiveRecord::Migration[5.0]
def change
create_table :stage_items do |t|
t.string :name
t.timestamps
end
create_table :stage_batches do |t|
t.timestamps
end
create_table :stage_batches_and_stage_items, id: false do |t|
t.belongs_to :stage_item, index: true
t.belongs_to :stage_batch, index: true
end
end
end
When I try to access a stage_batches
stage_items
I get the following error:
irb(main):002:0> StageBatch.first.stage_items
StageBatch Load (0.2ms) SELECT "stage_batches".* FROM "stage_batches" ORDER BY "stage_batches"."id" ASC LIMIT ? [["LIMIT", 1]]
StageItem Load (0.4ms) SELECT "stage_items".* FROM "stage_items" INNER JOIN "stage_batches_items" ON "stage_items"."id" = "stage_batches_items"."stage_item_id" WHERE "stage_batches_items"."stage_batch_id" = ? [["stage_batch_id", 1]]
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such table: stage_batches_items: SELECT "stage_items".* FROM "stage_items" INNER JOIN "stage_batches_items" ON "stage_items"."id" = "stage_batches_items"."stage_item_id" WHERE "stage_batches_items"."stage_batch_id" = ?
stage_batches_and_stage_items
to `
Is there something wrong with my migration?stage_batches_and_stage_items
to stage_batches_items
?Upvotes: 1
Views: 1080
Reputation: 34338
You have to follow the Rails naming convention for naming the joining table while using has_and_belongs_to_many
. Alternatively, you can specify a joining table name using join_table
option.
In your particular case, change the stage_batches_and_stage_items
's name to stage_batches_stage_items
. By default, the joining table has both table names (pluralized) in lexical order.
From the Rails guide [Section 3.3.2]:
If you create a has_and_belongs_to_many association, you need to explicitly create the joining table. Unless the name of the join table is explicitly specified by using the :join_table option, Active Record creates the name by using the lexical book of the class names. So a join between author and book models will give the default join table name of "authors_books" because "a" outranks "b" in lexical ordering.
I would recommend avoiding has_and_belongs_to_many
if this is not a hard requirement due to it's limitations (e.g., you can't add any extra columns to this joining table.) Instead, use has_many :through association. In that case, you can customize the joined table name easily. Also, you can add extra columns to the joining table as you need.
Here is how the code will look like using has_many :through
association:
class StageItem < ApplicationRecord
has_many :stage_item_batches
has_many :stage_batches, through: :stage_item_batches
end
# joining model
class StageItemBatch < ApplicationRecord
belongs_to :stage_item
belongs_to :stage_batch
end
class StageBatch < ApplicationRecord
has_many :stage_item_batches
has_many :stage_items, through: :stage_item_batches
end
The migrations for original two models will stay the same. You just need a migration for the joining model:
rails g model StageItemBatch stage_item:references stage_batch:references
Upvotes: 1