0

I have two models:

class RecordProduct < ApplicationRecord
end

class ShopiTagProduct < ApplicationRecord
end

And I want to create a many to many relationship between models. I execute this command:

rails g migration CreateJoinTableRecordProductsShopyTagProducts record_products:references:index shopi_tag_products:references:index

This command create this migration:

class CreateJoinTableRecordProductsShopyTagProducts < ActiveRecord::Migration[5.2]
    def change
        create_join_table :record_products, :shopi_tag_products do |t|
            t.references :record_products, foreign_key: true, index: {name: :productId}
            t.references :shopi_tag_products, foreign_key: true, index: {name: :tagId}
        end
    end
end

when execute rake db:migrate throws an error: 'name_index_tooooo_long' on table 'name_new_table' is too long; the limit is 64 characters.

So, I change the migration class to:

class CreateJoinTableRecordProductsShopyTagProducts < ActiveRecord::Migration[5.2]
    def change
        create_join_table :record_products, :shopi_tag_products do |t|
            t.references :record_products, foreign_key: true, index: {name: :productId}
            t.references :shopi_tag_products, foreign_key: true, index: {name: :tagId}
        end
    end
end

When I execute rake db:migrate it create the table, but with columns are repetead:

record_product_id
shopi_tag_product_id
record_products_id
shopi_tag_products_id

I have two question.

First: Whay the columns are repetead? What is the correct way for columns to be generated properly?

Second: Should I manually add the relations (has_and_belongs_to_many) in the models or are these added automatically?

victorpacheco3107
  • 822
  • 3
  • 10
  • 32

1 Answers1

1

I'm not sure that it's possible to add has_many into models automatically in your case, I add it manually by hand in such cases.

Running rails g migration CreateJoinTableRecordProductsShopyTagProducts record_products:references:index shopi_tag_products:references:index would generate this migrations:

class CreateJoinTableRecordProductsShopyTagProducts < ActiveRecord::Migration[5.2]
  def change
    create_join_table :record_products, :shopi_tag_products do |t|
      t.references :record_products, foreign_key: true
      t.references :shopi_tag_products, foreign_key: true
    end
  end
end

t.references are redundant here, since you already use create_join_table

rails g migration CreateJoinTableRecordProductsShopyTagProducts record_products shopi_tag_products will do the job. It will generate such file:

class CreateJoinTableRecordProductsShopyTagProducts < ActiveRecord::Migration[5.2]
  def change
    create_join_table :record_products, :shopi_tag_products do |t|
      # t.index [:record_product_id, :shopi_tag_product_id]
      # t.index [:shopi_tag_product_id, :record_product_id]
    end
  end
end

You need to uncomment indexes and assing names to them to solve problem with long index names.

As an alternative you can go this way: rails g model record_products_shopi_tag_products record_product:references shopi_tag_product:references It will generate:

class CreateRecordProductsShopiTagProducts < ActiveRecord::Migration[5.2]
  def change
    create_table :record_products_shopi_tag_products do |t|
      t.references :record_product, foreign_key: true
      t.references :shopi_tag_product, foreign_key: true

      t.timestamps
    end
  end
end

Note that here create_table is used instead of create_join_table, so in this case you have to write t.references. In this migration you have to add , index: {name: ...} to solve problem with long indexes.

I have written such models:
rails g model RecordProduct name:string
rails g model ShopiTagProduct name:string

record_product.rb

class RecordProduct < ApplicationRecord
  # has_and_belongs_to_many :shopi_tag_products

  # not needed if you use has_and_belongs_to_many
  has_many :record_products_shopi_tag_products, dependent: :destroy
  has_many :shopi_tag_products, through: :record_products_shopi_tag_products

  validates :name, presence: true
end

shopi_tag_product.rb

class ShopiTagProduct < ApplicationRecord
  # has_and_belongs_to_many :record_products

  # not needed if you use has_and_belongs_to_many
  has_many :record_products_shopi_tag_products, dependent: :destroy
  has_many :record_products, through: :record_products_shopi_tag_products

  validates :name, presence: true
end

record_products_shopi_tag_product.rb (not needed if you use has_and_belongs_to_many)

class RecordProductsShopiTagProduct < ApplicationRecord
  belongs_to :record_product
  belongs_to :shopi_tag_product
end

seeds.rb

RecordProduct.destroy_all
ShopiTagProduct.destroy_all

r = RecordProduct.create!(name: 'foo')
s = ShopiTagProduct.create!(name: 'bar')

r.shopi_tag_products << s
r.save!

p ActiveRecord::Base.connection.execute('select * from record_products_shopi_tag_products')
$ rake db:seed
[{"record_product_id"=>4, "shopi_tag_product_id"=>4}]

I would strongly suggest to use has_many through instead of has_and_belongs_to_many because has_and_belongs_to_many doesn't support dependent: destroy (habtm relationship does not support :dependent option)