2013-04-23 23 views
9

Chúng tôi có mối quan hệ đơn giản has_and_belongs_to_many giữa hai mô hình. Chúng tôi muốn thêm vào một số tham số cho mô hình đó, vì vậy chúng tôi cần phải thay đổi nó thành một has_many: thông qua loại mô hình.Cách di chuyển has_and_belongs_to_many sang has_many thông qua?

Như tôi biết, chúng ta cần phải thêm vào một cột id (cùng với bất kỳ cột nào chúng tôi muốn bổ sung). Tuy nhiên, tôi không rõ 100% về cách làm điều này. Nếu chúng ta thêm một cột số nguyên: id, đường ray sẽ biết rằng đó là khóa chính 'id'?

Chúng tôi đang sử dụng phiên bản 3.x.

Trả lời

7

Chỉ cần thêm id trên bàn của bạn trong việc di chuyển:

add_column :table, :id, :primary_key

tham chiếu từ this answer

+0

điều này có thể không phải là giải pháp tốt nhất cho một ứng dụng đã chạy trong sản xuất, đặc biệt là khi người dùng có thể cập nhật/chèn/xóa các bản ghi từ bảng một cách thường xuyên. – Todd

6

Có một bài đăng ở đây minh họa bằng cách sử dụng sql để vá bảng habtm và thêm id (làm pkey): Rails modeling: converting HABTM to has_many :through. Tôi đã gặp sự cố với cách tiếp cận đó và có thể có các sự cố cụ thể về db. Tôi đã kết thúc bằng cách trashing bảng join (habtm) và tạo một mô hình join mới. Điều đó hiệu quả.

Một số lưu ý: trước khi thực hiện việc này, tôi khuyên bạn nên tạo chi nhánh trong git và lưu trữ db để khôi phục dễ dàng nếu nó bị lệch sang một bên.

Đây là những bước sau:

  1. Sửa hai tham gia mô hình để sử dụng has_many qua

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

    Làm tương tự cho các bệnh nhân.

  2. Tạo tham gia mô hình mới:

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

    Chỉnh sửa các tập tin chuyển đổi mới được tạo ra ở trên ... Lưu ý rằng 'thay đổi' như là phương pháp di cư không hoạt động, vì chúng ta đang xử lý dữ liệu. Xem bên dưới.

  3. Tạo mới tham gia mô hình trong db và bản đồ tất cả các hiệp hội habtm cũ để has_many mới thông qua hồ sơ:

    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. Nếu nó có vẻ quá nhiều của một nỗi đau để tái tạo lại các hiệp hội habtm cũ, như theo hướng dẫn đường ray, chỉ cần hủy bỏ. Tuy nhiên, lưu ý rằng người ta không còn có thể khôi phục các di chuyển bằng cách tiếp cận này nữa.

    def self.down 
        raise ActiveRecord::IrreversibleMigration 
    end 
    

    Thay vào đó, để đi 'xuống', chỉ cần giết nhánh git và tải lại db dự phòng của bạn. Sau đó, bạn có thể tiếp tục db: rollback từ đó nếu cần thiết. Mặt khác, nếu has_many thông qua các hồ sơ không cần sửa đổi, cách tiếp cận khác là để chỉ việc kéo thả: id cột, và đổi tên db:

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

    tôi đã không kiểm tra sau này (như trong trường hợp của tôi, tôi làm phải gây rối với siêu dữ liệu). Những ý tưởng này đến từ bài đăng này: http://7fff.com/2007/10/31/activerecord-migrating-habtm-to-model-table-suitable-for-has_many-through/.

+0

Cảm ơn bạn Monty, giúp bạn tiết kiệm tôi. cảm ơn bạn rất nhiều – Zakaria

11

Giả sử bảng cơ sở dữ liệu của bạn cho has_and_belong_to nhiều tạo ra bởi đường ray là patients_physicians

Tất cả bạn cần làm là tạo một mô hình như thế này

rails g model patients_physician --skip-migration

sau đó bạn có thể thêm bất cứ điều gì cột mà bạn cần với các lệnh chuyển đổi của bạn như làm

rails g migration add_new_column_to_patients_physician new_column 

và dữ liệu của bạn vẫn sẽ còn nguyên vẹn và bạn có thể làm truy vấn của bạn dựa tấn các mô hình bạn được tạo.

Dont quên thêm

belongs_to :model_1 
belongs_to :model_2 

với mô hình patients_physician mới được bổ sung

sau đó bạn sẽ có quyền truy cập để làm trong mô hình bạn cần.

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

Câu trả lời rất hay. Đơn giản chỉ cần áp dụng và không có tác dụng phụ. Tất cả các chức năng trước đó được cung cấp bởi liên kết 'habtm' sẽ vẫn có sẵn. (như '<<' hoặc 'clear') Điểm duy nhất để nhận xét là tên của mô hình mới mà * phải * như bạn đã viết:' bệnh nhân_physician' số ít, không số nhiều. –

4

Đây là di cư tôi đã sử dụng để chuyển đổi một mối quan hệ has_and_belongs_to_many giữa các nhóm và người sử dụng:

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

Đây là câu trả lời hay vì nó đang giữ dữ liệu khi quay lại! –

Các vấn đề liên quan