2013-04-23 55 views
9

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

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

我们使用最新的3.x.

回答

7

就在迁移你的桌子上添加ID:

add_column :table, :id, :primary_key

this answer

+0

对于已经在生产中运行的应用程序,这可能不是最好的解决方案,特别是当用户可能定期更新/插入/删除表中的记录时。 – Todd 2016-06-20 19:49:34

6

这里有一个帖子,说明如何使用sql来修补habtm表并添加一个id(作为pkey):Rails modeling: converting HABTM to has_many :through。我遇到了这种方法的麻烦,并且可能有数据库特定的问题。我最终抛弃了join(habtm)表并创建了一个新的连接模型。这工作。

一些注意事项:在做这件事之前,我建议在git中创建一个分支,并将数据库归档,以便在恢复时轻松恢复。

这些是步骤:

  1. 编辑两种连接模型通过

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

    使用的has_many执行相同的患者。

  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协会评为根据导轨指南,只需中止。但是请注意,用这种方法不能再回滚迁移。

    def self.down 
        raise ActiveRecord::IrreversibleMigration 
    end 
    

    相反,要'下',只需要杀死git分支,然后重新加载备份数据库。然后,如果需要,可以从那里恢复db:rollback。在另一方面,如果通过记录的has_many不需要修改,另一种方法是刚落: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/

+0

谢谢蒙蒂,你救了我。非常感谢 – Zakaria 2015-06-08 04:19:41

11

引用假设你的数据库表上,由铁轨产生的has_and_belong_to许多是patients_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 

添加到新添加的模型patients_physician

,那么你将有机会获得你需要的模型做 。

has_many patients_physician 
has_many :model_2, through: :patients_physician 
+0

非常好的答案。只需申请,无副作用。 'habtm'关联给出的所有以前的函数将仍然可用。 (如'<<或'clear') 唯一需要注意的是新模型的名称*必须*是您写的:'patients_physician'单数,而不是复数。 – 2016-03-29 18:29:00

4

这是我用于转换团队和用户之间的关系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 
+0

这是一个很好的答案,因为它在回滚时保留数据! – 2016-02-19 10:38:38

相关问题