如何将has_and_belongs_to_many迁移到has_many?

时间:2013-04-23 00:55:07

标签: ruby-on-rails

我们在两个模型之间有一个简单的has_and_belongs_to_many关系。我们想在该模型中添加一些参数,因此我们需要将其更改为has_many:通过某种模型。

据我所知,我们需要添加一个id列(以及我们想要的任何列)。但是,我不清楚100%如何做到这一点。如果我们添加一个整数列:id,那么rails会知道那是'id'主键吗?

我们正在使用最新的3.x.

4 个答案:

答案 0 :(得分:12)

假设rails创建的has_and_belong_to数据库表是users_physicians

您需要做的就是生成一个像这样的模型

<强> rails g model patients_physician --skip-migration

然后您可以使用迁移命令添加所需的任何列,例如“

rails g migration add_new_column_to_patients_physician new_column

并且您的数据仍然完好无损,您可以根据自己生成的模型进行查询。

别忘了添加

belongs_to :model_1
belongs_to :model_2 

新增加的模型患者_医师

然后你就可以访问了 在你需要的模型中。

has_many patients_physician
has_many :model_2, through: :patients_physician

答案 1 :(得分:9)

只需在迁移中为您的表添加ID:

add_column :table, :id, :primary_key

引自this answer

答案 2 :(得分:6)

这里有一篇文章说明使用sql修补habtm表并添加一个id(作为一个pkey):Rails modeling: converting HABTM to has_many :through。我遇到了这种方法的问题,可能存在特定于数据库的问题。我最终破坏了join(habtm)表并创建了一个新的连接模型。那很有效。

一些警告:在此之前,我建议在git中创建一个分支并归档数据库,以便在它横向移动时轻松恢复。

以下是这些步骤:

  1. 编辑两个已连接的模型以使用has_many到

    class Physician < ActiveRecord::Base
      # has_and_belongs_to_many :patients
      has_many :appointments
      has_many :patients, :through => :appointments
    end
    

    为患者做同样的事。

  2. 创建新的连接模型:

    rails g model Appointment physician_id:integer patient_id:integer has_insurance:boolean
    

    编辑上面生成的新迁移文件...请注意,由于我们正在处理数据,因此迁移方法的“更改”不起作用。见下文。

  3. 在数据库中创建新的连接模型,并通过记录将所有旧的habtm关联映射到新的has_many:

    def self.up
      create_table :appointments do |t|
        t.integer :physician_id
        t.integer :patient_id
        t.boolean :has_insurance
    
        t.timestamps
      end
    
      Physician.find_each {|doc|
        doc.patients.each do |pat|
          Appointment.create!(:physician_id => doc.id, :patient_id => pat.id, :has_insurance => false )
        end
      }
    
      # finally, dump the old hatbm associations
      drop_table :patients_physicians
    
    end
    
  4. 如果重建旧的habtm协会似乎太痛苦了,按照rails指南,只是中止。但请注意,使用此方法无法再回滚迁移。

    def self.down
      raise ActiveRecord::IrreversibleMigration
    end
    

    相反,要“关闭”,只需杀死git分支,然后重新加载备份数据库。然后,如有必要,您可以从那里恢复db:rollback。另一方面,如果has_many through记录不需要修改,另一种方法是删除:id列,并重命名db:

    def self.down
      remove_column :appointments, :id
      rename_table :appointments, :patients_physicians
    end
    

    我没有测试后者(在我的情况下,我必须弄乱元数据)。这些想法来自这篇文章:http://7fff.com/2007/10/31/activerecord-migrating-habtm-to-model-table-suitable-for-has_many-through/

答案 3 :(得分:5)

这是我用于转换团队和用户之间has_and_belongs_to_many关系的迁移:

class CreateTeamMembers < ActiveRecord::Migration
  def up
    # Create a new table with id and timestamps to replace the old one
    create_table :team_members do |t|
      t.belongs_to :team
      t.belongs_to :user
      t.timestamps
    end

    # Now populate it with a SQL one-liner!
    execute "insert into team_members(team_id,user_id) select team_id,user_id from teams_users"

    # drop the old table
    drop_table :teams_users
  end

  def down
    # This leaves the id and timestamps fields intact
    rename_table :team_members, :teams_users
  end
end