Un paio di volte sono stato nella situazione in cui volevo refactoring il design di alcuni modelli e ho finito per mettere la logica di aggiornamento nelle migrazioni. Tuttavia, per quanto ho capito, questa non è una buona pratica (soprattutto perché sei incoraggiato a usare il tuo file di schema per la distribuzione, e non le tue migrazioni). Come gestisci questo tipo di problemi?Inserimento della logica di aggiornamento nelle migrazioni
Per chiarire cosa intendo, dire che ho un modello Utente. Poiché pensavo che ci sarebbero stati solo due tipi di utenti, vale a dire un utente "normale" e un amministratore, ho scelto di utilizzare un semplice campo booleano che diceva se l'utente era un amministratore o meno.
Tuttavia, dopo che ho pensato di aver bisogno di un terzo tipo di utente, forse un moderatore o qualcosa di simile. In questo caso aggiungo un modello UserType (e la migrazione corrispondente) e una seconda migrazione per rimuovere il flag "admin" dalla tabella utente. E qui arriva il problema. Nella migrazione "add_user_type_to_users" devo mappare il valore del flag admin su un tipo di utente. Inoltre, per fare ciò, i tipi di utenti devono esistere, ovvero non posso usare il file di semi, ma piuttosto creare i tipi di utente nella migrazione (anch'essi considerati cattive pratiche). Ecco un codice fittizio che rappresenta la situazione:
class CreateUserTypes < ActiveRecord::Migration
def self.up
create_table :user_types do |t|
t.string :name, :nil => false, :unique => true
end
#Create basic types (can not put in seed, because of future migration dependency)
UserType.create!(:name => "BASIC")
UserType.create!(:name => "MODERATOR")
UserType.create!(:name => "ADMINISTRATOR")
end
def self.down
drop_table :user_types
end
end
class AddTypeIdToUsers < ActiveRecord::Migration
def self.up
add_column :users, :type_id, :integer
#Determine type via the admin flag
basic = UserType.find_by_name("BASIC")
admin = UserType.find_by_name("ADMINISTRATOR")
User.all.each {|u| u.update_attribute(:type_id, (u.admin?) ? admin.id : basic.id)}
#Remove the admin flag
remove_column :users, :admin
#Add foreign key
execute "alter table users add constraint fk_user_type_id
foreign key (type_id) references user_types (id)"
end
def self.down
#Re-add the admin flag
add_column :users, :admin, :boolean, :default => false
#Reset the admin flag (this is the problematic update code)
admin = UserType.find_by_name("ADMINISTRATOR")
execute "update users set admin=true where type_id=#{admin.id}"
#Remove foreign key constraint
execute "alter table users drop foreign key fk_user_type_id"
#Drop the type_id column
remove_column :users, :type_id
end
end
Come potete vedere ci sono due parti problematiche. Prima la parte di creazione riga nel primo modello, che è necessaria se si desidera eseguire tutte le migrazioni in una riga, quindi la parte "aggiornamento" nella seconda migrazione che associa la colonna "admin" alla colonna "type_id".
Qualche consiglio?
Quando si tratta di chiavi esterne, trovo che siano molto utili, specialmente quando si tratta di dati di importanza strategica. Mi aiuta a evitare i dati penzolanti ea rilevare se mi sono perso l'applicazione di ganci e simili dal lato di Rails. Anche se hai ragione, potrebbero darti dei brutti messaggi di errore. –
Vedo, è una questione di scelta. se l'integrità dei dati oltre i backup è superiore a UX, fk è la strada da percorrere. In tal caso si dispone di un plug-in per la gestione di fk dalle migrazioni: http://agilewebdevelopment.com/plugins/foreign_key_migrations – Oinak
Ok. Grazie per il suggerimento! –