Rails has_and_belongs_to_many 迁移

Posted

技术标签:

【中文标题】Rails has_and_belongs_to_many 迁移【英文标题】:Rails has_and_belongs_to_many migration 【发布时间】:2011-09-27 12:39:51 【问题描述】:

我有两个模型 restaurantuser 我想执行 has_and_belongs_to_many 关系。

我已经进入模型文件并添加了has_and_belongs_to_many :restaurantshas_and_belongs_to_many :users

我认为此时我应该能够使用 Rails 3 做类似的事情:

rails generate migration ....

但我尝试过的一切似乎都失败了。我确信这是非常简单的事情,我是 Rails 新手,所以我还在学习。

【问题讨论】:

【参考方案1】:

您需要添加一个单独的连接表,其中只有一个restaurant_iduser_id(无主键),按字母顺序

首先运行您的迁移,然后编辑生成的迁移文件。

Rails 3

rails g migration create_restaurants_users_table

Rails 4

rails g migration create_restaurants_users

Rails 5

rails g migration CreateJoinTableRestaurantUser restaurants users

来自docs:

还有一个生成器,如果 JoinTable 会生成连接表 是名称的一部分:


您的迁移文件(注意 :id => false;这是阻止创建主键的原因):

Rails 3

class CreateRestaurantsUsers < ActiveRecord::Migration
  def self.up
    create_table :restaurants_users, :id => false do |t|
        t.references :restaurant
        t.references :user
    end
    add_index :restaurants_users, [:restaurant_id, :user_id]
    add_index :restaurants_users, :user_id
  end

  def self.down
    drop_table :restaurants_users
  end
end

Rails 4

class CreateRestaurantsUsers < ActiveRecord::Migration
  def change
    create_table :restaurants_users, id: false do |t|
      t.belongs_to :restaurant
      t.belongs_to :user
    end
  end
end

t.belongs_to 将自动创建必要的索引。 def change 将自动检测向前或回滚迁移,无需向上/向下。

Rails 5

create_join_table :restaurants, :users do |t|
  t.index [:restaurant_id, :user_id]
end

注意:还有一个自定义表名选项,可以作为参数传递给 create_join_table,名为 table_name。来自docs

默认情况下,连接表的名称来自 提供给 create_join_table 的前两个参数,按字母顺序排列 命令。要自定义表的名称,请提供 :table_name 选项:

【讨论】:

@Dex - 出于好奇,您能否解释一下为什么要使用第二个复合索引,用反向列顺序定义?我的印象是列顺序无关紧要。我不是DBA,只是想进一步了解自己。谢谢! @Jimbo 你不需要那样,它真的取决于你的查询。索引从左到右读取,因此如果您在 restaurant_id 上搜索,第一个索引将是最快的。如果您在 user_id 上搜索,第二个会有所帮助。如果您同时搜索两者,我认为数据库足够智能,只需要一个。所以我想第二个真的不需要复合。这只是一个例子。这是一个 Rails 问题,所以在 DB 部分发布会产生更完整的答案。 第二个索引有一些冗余 - 只要您同时查询 restaurant_id 和 user_id,它们在 SQL 中的顺序无关紧要,将使用第一个索引。如果您只查询 restaurant_id,也将使用它。第二个索引只需要在 :user_id 上,并且会在您只查询 user_id 的情况下使用(由于其键的顺序,第一个索引无济于事)。 在 Rails 4 中,迁移必须是 rails g migration create_restaurants_users,最后没有 table 您也可以使用 rails g migration CreateJoinTableRestaurantUser 餐厅用户。阅读guides.rubyonrails.org/migrations.html#creating-a-join-table【参考方案2】:

这里的答案已经过时了。从 Rails 4.0.2 开始,您的迁移使用 create_join_table

要创建迁移,请运行:

rails g migration CreateJoinTableRestaurantsUsers restaurant user

这将生成以下内容:

class CreateJoinTableRestaurantsUsers < ActiveRecord::Migration
  def change
    create_join_table :restaurants, :users do |t|
      # t.index [:restaurant_id, :user_id]
      # t.index [:user_id, :restaurant_id]
    end
  end
end

如果您想索引这些列,请取消注释相应的行,一切顺利!

【讨论】:

我通常会取消注释其中一个索引行并将unique: true 添加到其中。这将防止创建重复的关系。【参考方案3】:

对于 HABTM 关系,您需要创建一个连接表。只有连接表,该表不应有 id 列。试试这个迁移。

def self.up
  create_table :restaurants_users, :id => false do |t|
    t.integer :restaurant_id
    t.integer :user_id
  end
end

def self.down
  drop_table :restaurants_users
end

你必须检查this relationship rails guide tutorials

【讨论】:

我认为您不需要模型,并且我在链接中没有看到任何关于需要模型来建立 HABTM 关系的内容。 要加快生成查询的速度,请在 id 字段中添加索引。【参考方案4】:

在创建连接表时,请注意迁移名称/类中两个表需要按字母顺序列出的要求。如果您的型号名称相似,这很容易咬到您,例如“abc”和“abb”。如果你要跑步

rails g migration create_abc_abb_table

您的关系将不会按预期工作。你必须使用

rails g migration create_abb_abc_table

改为。

【讨论】:

哪个先出现? foo 还是 foo_bar? 在 Rails 控制台中运行:["foo_bar", "foo", "foo bar"].sort # => ["foo", "foo bar", "foo_bar"] Unix 排序来了一样。

以上是关于Rails has_and_belongs_to_many 迁移的主要内容,如果未能解决你的问题,请参考以下文章

运行不同版本的rails gems

Rails:电子邮件“deliver_”方法的自定义模板?

禁用 Rails 4 的 ActiveRecord

应用程序错误:Heroku,Ruby on Rails 4,警告:rails_12factor,vendor / bundle,no Procfile

如何使用Rails中的Mobility gem以一种形式实现所有翻译? [关闭]

Rails - 设计和行为_as_audited