2

There are two models within a namespace, and they need to be associated in a many-to-many relationship with has_and_belongs_to_many. They are created like so:

$ rails generate model namespace/model_a name:string

# app/models/namespace/model_a.rb
class Namespace::ModelA < ActiveRecord::Base
end

and

$ rails generate model namespace/model_b name:string

# app/models/namespace/model_b.rb
class Namespace::ModelB < ActiveRecord::Base
end

What do I need to do successfully establish the many-to-many relationship?

rodamn
  • 2,191
  • 19
  • 24

1 Answers1

6

I had a lot of issues getting this to work, but through a lot of parsing the Rails server error messages and a database client, I was able to massage this into working. Here is what I did:

With Rails 4, the association can be created with the has_and_belongs_to_many relationship in the models and with create_join_table in a separate migration. Of course, using the rails generators fall short of our goal. Also note that create_join_table works differently and may not even be available in previous versions of Rails.

Step 1. Generate a migration for the join table without the namespaces. We'll adjust it later.

$ rails g migration CreateModelAModelBJoinTable model_a model_b

# db/migrate/<somenumbers>_create_model_a_model_b_join_table.rb
class CreateModelAModelBJoinTable < ActiveRecord::Migration
  def change
    create_join_table :model_as, :model_bs do |t|
      # t.index [:model_a_id, :model_b_id]
      # t.index [:model_b_id, :model_a_id]
    end
  end
end

The migration will need some massaging. We'll need to:

  1. Override the table name to account for namespaces
  2. Optionally, select an index. Tip: If your namespaces and model names are long, you may have to override the index name if it goes over the character limit imposed by your database.

Here's the final join table migration, noting the inclusion of the namespaces in the table name, but excluding the namespace in the indices. Simply, this will name the table and its indices the way that rails has_and_belongs_to_many will expect.

# db/migrate/<somenumbers>_create_model_a_model_b_join_table.rb
class CreateModelAModelBJoinTable < ActiveRecord::Migration
  def change
    create_join_table :model_as, :model_bs, table_name: :namespace_model_as_namespace_model_bs do |t|
      t.index [:model_a_id, :model_b_id], name: :index_namespace_model_ab # or whatever, according to your database naming scheme
      # t.index [:model_b_id, :model_a_id]
    end
  end
end

Step 2. Set up our models with the has_and_belongs_to_many associations.

In addition to the association, we need to override the class name and join table name.

# app/models/namespace/model_a.rb
class Namespace::ModelA < ActiveRecord::Base
  has_and_belongs_to_many :namespace_model_b, class_name: "Namespace:ModelB", join_table: "namespace_model_as_namespace_model_bs"
end

Step 3. Run the database migrations

rake db:migrate

Final tips if you encounter issues:

You can always back out a migration, delete it, and start over as such:

$ rake db:rollback STEP=1
$ rails d migration CreateModelAModelBJoinTable

Hope this helps!

rodamn
  • 2,191
  • 19
  • 24
  • In your step 1's code you forgot to add colon (:) before the table name. It should be table_name: :namespace_model_as_namespace_model_bs. Anyways, your answer helped me! – VPaul Jan 24 '18 at 15:32