Create join table with no primary key
Asked Answered
L

3

12

I have two tables with a many to many relationship that I am using has_and_belongs_to_many to define the association.

class Foo < ActiveRecord::Base
  ...
  has_and_belongs_to_many :bar
  ...
end

class Bar < ActiveRecord::Base
  ...
  has_and_belongs_to_many :foo
  ...
end

I also have the class defined to represent the join table

class BarFoo < ActiveRecord::Base
  ...
  belongs_to :foo
  belongs_to :bar
  ...
end

When I run rake db:seed I get the following error:

Primary key is not allowed in a has_and_belongs_to_many join table (bar_foo)

If I edit the database and remove the primary key field (ID) from the bar_foo table and then rerun rake db:seed everything works as desired.

Given the above, what is the preferred means of creating join tables in rails with no primary key?

I also tried using "has_many :bars, :through => :foo" and vise versa but got an error message something like "undefined method 'klass' for nil:NilClass".

Lucrece answered 22/10, 2010 at 14:38 Comment(0)
Q
24

Yes, primary key is not allowed for has_and_belongs_to_many.

You have 2 ways to solve this:

Remove the primary key on that table. In your migration class:

create_table :bar_foo, :id => false do |t|
  t.integer :bar_id
  t.integer :foo_id
end

Apart from this, you will have to delete the file bar_foo.rb from app/models and also remove any fixture and test files that might have been generated. A good idea is to call the script/destroy (or rails destroy) to destroy the files and then regenerating the migration.

Or convert to has_many :through

class Foo < ActiveRecord::Base
  ...
  has_many :bar_foos
  has_many :bars, :through => :bar_foos
  ...
end

class Bar < ActiveRecord::Base
  ...
  has_many :bar_foos
  has_many :foos, :through => :bar_foos
  ...
end

class BarFoo < ActiveRecord::Base
  ...
  belongs_to :foo
  belongs_to :bar
  ...
end
Quitclaim answered 22/10, 2010 at 14:50 Comment(0)
T
5

You don't need the model

class BarFoo < ActiveRecord::Base
  ...
  belongs_to :foo
  belongs_to :bar
  ...
end

the has_and_belongs_to_many association will search for a table called bar_foo in your database what you need to do is generate a migration to create this table.

rails generate migration add_table_bar_foo_for_association

then you edit your migration and it should look like this

class AddTableBarFooForAssociation < ActiveRecord::Migration
  def up
    create_table :bar_foo, :id => false do |t|
        t.references :bar
        t.references :foo
    end
  end

  def down
    drop_table :bar_foo
  end
end

Now your association should work and also if you need the association to have extra attributes on the join you can use the has_many :through way and create a model associated to this.

Tourmaline answered 21/10, 2011 at 5:14 Comment(0)
H
4

If you want to use a HABTM association you shouldn't create a model for it - just a table bars_foos with bar_id and foo_id integer columns.

If you need the model in between (e.g. if you want to keep track of created_at or some other attributes of the relation) you can add additional model e.g. Barred and then you'd have:

class Foo < ActiveRecord::Base
  ...
  has_many :bars, :through => :barred
  ...
end

class Bar < ActiveRecord::Base
  ...
  has_many :foos, :through => :barred
  ...
end

class Barred < ActiveRecord::Base
  has_many :bars
  has_many :foos
end
Hammerskjold answered 22/10, 2010 at 14:42 Comment(2)
Matt, thanks for the response. I'm relatively new to RoR so what is the preferred means of creating a table with no corresponding model files? Would this just be done by generating a migration file and using create_table to define the table with the two attributes bar_id and foo_id?Lucrece
Here's a nice answer how to do it: Do I need to manually create a migration for a HABTM join table?Hammerskjold

© 2022 - 2024 — McMap. All rights reserved.