Rails-在模型上自动添加关系

时间:2019-07-12 23:02:24

标签: ruby-on-rails ruby rake dbmigrate

我有两个模型:

class RecordProduct < ApplicationRecord
end

class ShopiTagProduct < ApplicationRecord
end

我想在模型之间建立多对多关系。我执行以下命令:

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

此命令创建此迁移:

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

在执行rake db:migrate时抛出错误:表'name_new_table'上的'name_index_tooooo_long'太长;限制为64个字符。

因此,我将迁移类更改为:

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

当我执行rake db:migrate时,它会创建表,但要重复列:

record_product_id
shopi_tag_product_id
record_products_id
shopi_tag_products_id

我有两个问题。

首先:重复这些列吗?正确生成列的正确方法是什么?

第二:我应该在模型中手动添加关系(has_and_belongs_to_many)还是自动添加这些关系?

1 个答案:

答案 0 :(得分:1)

我不确定是否可以根据您的情况自动将has_many添加到模型中,在这种情况下我会手动添加。

运行rails g migration CreateJoinTableRecordProductsShopyTagProducts record_products:references:index shopi_tag_products:references:index 会产生以下迁移:

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在这里是多余的,因为您已经在使用create_join_table

rails g migration CreateJoinTableRecordProductsShopyTagProducts record_products shopi_tag_products将完成工作。 它将生成这样的文件:

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

您需要取消注释索引并为它们添加名称,以解决索引名称过长的问题。

您可以选择以下方式: rails g model record_products_shopi_tag_products record_product:references shopi_tag_product:references 它将生成:

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

请注意,这里使用create_table代替create_join_table,因此在这种情况下,您必须编写t.references。 在此迁移中,您必须添加, index: {name: ...}才能解决长索引问题。

我已经写过这样的模型:
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(如果您使用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}]

我强烈建议使用has_many through而不是has_and_belongs_to_many,因为has_and_belongs_to_many不支持dependent: destroyhabtm relationship does not support :dependent option