2013-04-23 13 views
9

Mamy prostą relację has_and_belongs_to_many między dwoma modelami. Chcielibyśmy dodać pewne parametry do tego modelu, więc musimy zmienić go na has_many: poprzez rodzaj modelu.Jak przeprowadzić migrację has_and_belongs_to_many do has_many through?

Jak wiem, musimy dodać kolumnę id (wraz z kolumnami, które chcemy dodatkowo). Jednak nie jestem w 100% przekonany, jak to zrobić. Jeśli dodamy kolumnę całkowitą: id, czy szyny będą wiedzieć, że jest to klucz podstawowy "id"?

Używamy najnowszego pliku 3.x.

Odpowiedz

7

Wystarczy dodać identyfikator na stole w migracji:

add_column :table, :id, :primary_key

odwoływać od this answer

+0

może to nie być najlepsze rozwiązanie dla aplikacji już uruchomionej w produkcji, szczególnie gdy użytkownicy mogą regularnie aktualizować/wstawiać/usuwać zapisy z tabeli. – Todd

6

Oto post, który ilustruje użycie sql do łatania tabeli habtm i dodawania id (jako pkey): Rails modeling: converting HABTM to has_many :through. Miałem problemy z tym podejściem i mogą istnieć problemy związane z db. Zakończyłem niszczenie tabeli join (habtm) i tworzenie nowego modelu łączenia. To się udało.

Niektóre zastrzeżenia: przed wykonaniem tej czynności zalecam utworzenie odgałęzienia w git i archiwizację bazy danych dla łatwego odzyskiwania, jeśli przejdzie on w bok.

Były to kroki:

  1. edytować dwóch połączonych modeli używać has_many poprzez

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

    Zrób to samo dla pacjentów.

  2. Tworzenie nowego przyłączenia model:

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

    Edycja nowy plik migracji generowane powyżej ... Zauważ, że „zmiany” jako metoda migracji nie działa, ponieważ są przetwarzania danych. Zobacz poniżej.

  3. stworzyć nowy model, w db i odwzorować wszystkie stare skojarzenia HABTM do nowego has_many poprzez zapisy:

    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. Jeśli wydaje się zbyt wiele bólu, aby zrekonstruować stare skojarzenia HABTM, jak według przewodnika po szynach, po prostu przerwij. Pamiętaj jednak, że nie można już wycofywać migracji za pomocą tego podejścia.

    def self.down 
        raise ActiveRecord::IrreversibleMigration 
    end 
    

    Zamiast tego, aby przejść w dół, po prostu zabij gałąź git i ponownie załaduj kopię zapasową db. W razie potrzeby możesz wznowić program db: rollback. Z drugiej strony, jeśli has_many poprzez zapisy nie wymagają modyfikacji, innym sposobem jest po prostu upuść: kolumna id i zmień nazwę DB:

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

    Nie przetestować ten ostatni (jak w moim przypadku zrobić muszą zadzierać z metadanymi). Pomysły te pochodziły z tego posta: http://7fff.com/2007/10/31/activerecord-migrating-habtm-to-model-table-suitable-for-has_many-through/.

+0

Dziękuję Monty, ty mnie uratować. bardzo dziękuję – Zakaria

11

Zakładając tabeli bazy danych dla has_and_belong_to wielu utworzonego przez szyny jest patients_physicians

Wszystkie musisz wygenerować model taki jak ten

rails g model patients_physician --skip-migration

następnie można dodać cokolwiek kolumna trzeba z poleceń migracja jak zrobić

rails g migration add_new_column_to_patients_physician new_column 

a Twoje dane będą nadal nienaruszone i można zrobić ton oparte kwerendy modelu wygenerowany.

nie zapomnij dodać

belongs_to :model_1 
belongs_to :model_2 

do nowo dodanego modelu patients_physician

wtedy będziesz miał dostęp do roboty w modelu trzeba.

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

Bardzo dobra odpowiedź. Po prostu do zastosowania i bez efektów ubocznych. Wszystkie poprzednie funkcje podane przez stowarzyszenie "habtm" będą nadal dostępne. (jak '<<' lub 'clear') Jedyną uwagą jest nazwa nowego modelu, który * musi * być taki jak napisałeś:' patients_physician' singular, a nie w liczbie mnogiej. –

4

To jest migracja użyłem do konwersji relację has_and_belongs_to_many między zespołami i Użytkowników:

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

To dobra odpowiedź, ponieważ zatrzymuje dane po wycofaniu! –

Powiązane problemy