2011-02-12 13 views
20

Sto cercando modi per scrivere le migrazioni in rotaie che possono essere eseguite contro il database molte volte senza fallire.Come scrivere le migrazioni condizionali nelle rotaie?

Per esempio diciamo che ho questa migrazione:

class AddUrlToProfile < ActiveRecord::Migration 
    def self.up 
    add_column :profile, :url, :string 
    end 

    def self.down 
    remove_column :profile, :url 
    end 
end 

Se la colonna url esiste già nella tabella Profile (se lo schema.rb è stato modificato in modo imprevisto, per esempio), la mia migrazione avrà esito negativo dicendo che è un duplicato!

Quindi, come eseguire questa migrazione solo se necessario?

Grazie

risposta

46

Si può fare qualcosa di simile:

class AddUrlToProfile < ActiveRecord::Migration 
    def self.up 
    Profile.reset_column_information 
    add_column(:profile, :url, :string) unless Profile.column_names.include?('url') 

    end 

    def self.down 
    Profile.reset_column_information 
    remove_column(:profile, :url) if Profile.column_names.include?('url') 
    end 
end 

Questo resetta le informazioni di colonna prima del suo inizio - fare in modo che il modello di profilo ha le informazioni di colonna up-to-date dal tavolo effettivo. Quindi aggiungerà la colonna solo se non esiste. La stessa cosa accade per la funzione down, ma rimuove solo la colonna se esiste.

Se si dispone di più casi d'uso per questo, è possibile includere il codice in una funzione e riutilizzarlo nelle migrazioni.

+0

bella soluzione! Grazie! –

+2

FYI: in genere è una cattiva idea fare riferimento esplicitamente ai modelli nella propria migrazione: cosa accadrebbe se quel modello venisse rimosso in futuro? O cambiamenti? Invece, crea un modello monouso aggiungendo '' 'profilo di classe

8

Questo dovrebbe funzionare

def self.table_exists?(name) 
    ActiveRecord::Base.connection.tables.include?(name) 
end 

if table_exists?(:profile) && !Profile.column_names.include?("url") 
    add_column :profile, :url, :string 
end 
+0

Grazie Fernando! –

15

Per Rails 3.X, c'è il metodo column_exists?(:table_name, :column_name).

Per Rails 2.X, è possibile verificare l'esistenza di colonne con il seguente:

columns("<table name>").index {|col| col.name == "<column name>"} 

... o se non sei in un file di migrazione:

ActiveRecord::Base.connection.columns("<table name>").index {|col| col.name == "<column name>"} 

Se restituisce zero, nessuna di queste colonne esiste. Se restituisce un Fixnum, la colonna esiste. Naturalmente, si può mettere i parametri più selettivi tra l'{...} se si vuole identificare una colonna di oltre solo il suo nome, per esempio:

{ |col| col.name == "foo" and col.sql_type == "tinyint(1)" and col.primary == nil } 
2

confezionamento mia migrazione in un condizionale lavorato per me. Rails 4.X.

class AddUrlToProfile < ActiveRecord::Migration 
    unless Profile.column_names.include?("url") 
    def self.up 
     add_column :profile, :url, :string 
    end 

    def self.down 
     remove_column :profile, :url 
    end 
    end 
end 
Problemi correlati