Rails 3.1 HABTM association with custom foreign_key generates wrong join statement
Question
I've encountered a rather frustrating problem when trying to use has_and_belongs_to_many associations.
The scenario is as follows.
I have a product that has many news items associated and vice versa. News items can be translated to different languages, so in order to keep track of news with same content (but translated to different language) - I've added a news_id to news.
My problem is that the association is between a product and a unique news (the newsitem.news_id) and not on the single news item (newsitem.id).
My models:
class Product < ActiveRecord::Base
has_and_belongs_to_many :newsitems , :association_foreign_key => :news_id
end
class Newsitem < ActiveRecord::Base
has_and_belongs_to_many :products, :foreign_key => :news_id
end
My migrations are as follows:
def change
create_table :products do |t|
t.string :name
t.timestamps
end
end
def change
create_table :newsitems do |t|
t.string :content
t.integer :news_id
t.integer :language_id
t.timestamps
end
end
def change
create_table :newsitems_products, :id => false do |t|
t.integer :news_id
t.integer :product_id
end
end
Using this setup I get the following correct sql generated when calling:
news = Newsitem.first
news.products.to_sql
SQL:
"SELECT `products`.* FROM `products`
INNER JOIN `newsitems_products`
ON `products`.`id` = newsitems_products`.`product_id`
WHERE `newsitems_products`.`news_id` = 1"
The troubles begin when I ask for all newsitems associated with product: prod = Products.first prod.newsitems.to_sql SQL:
"SELECT `newsitems`.* FROM `newsitems`
INNER JOIN `newsitems_products`
ON `newsitems`.`id` = `newsitems_products`.`news_id`
WHERE `newsitems_products`.`product_id` = 1"
Eventhough I've declared :association_foreign_key => :news_id on product and :foreign_key => :news_id on newsitem the generate "ON newsitems
.id
" is wrong and should be:
ON `newsitems`.`news_id` = `newsitems_products`.`news_id`
I hope some of you can crack this nut open.
Thanks in advance - Peter Piper
Solution
I had the same problem, I'll write a similar example.
1.First model:
class Label < ActiveRecord::Base
has_and_belongs_to_many :spaces
end
2.Second model:
class Space < ActiveRecord::Base
has_and_belongs_to_many :labels
end
3. Migration:
rails g migration createSpacesLabels
Edit the migration:
class CreateSpacesLabels < ActiveRecord::Migration def up create_table :spaces_labels, :id => false do |t| t.references :space t.references :label end add_index :spaces_labels, [:label_id, :space_id] add_index :spaces_labels, [:space_id, :label_id] end def down drop_table :spaces_labels end end
Problems:
I found that rails searches the wrong table, it looks for labelsspaces instead of spaceslabels, I guess it's because the name of the models. I solved it by adding in the models:
has_and_belongs_to_many :labels,:join_table=>'spaces_labels' has_and_belongs_to_many :spaces,:join_table=>'spaces_labels'
Now, you should be able, to perform queries like @space.labels or @label.spaces. I hope it helps.