Ich war schon ein paar Mal in der Situation, dass ich das Design eines Modells überarbeiten wollte und die Aktualisierungslogik in Migrationen unterbringen musste. Soweit ich das verstanden habe, ist das jedoch keine gute Praxis (vor allem, da Sie aufgefordert werden, Ihre Schemadatei für die Bereitstellung zu verwenden und nicht Ihre Migrationen). Wie gehen Sie mit dieser Art von Problemen um?
Um zu verdeutlichen, was ich meine, sagen wir, ich habe ein Benutzermodell. Da ich davon ausging, dass es nur zwei Arten von Benutzern geben würde, nämlich einen "normalen" Benutzer und einen Administrator, habe ich mich für ein einfaches boolesches Feld entschieden, das angibt, ob der Benutzer ein Administrator ist oder nicht.
Nach einer Weile dachte ich jedoch, dass ich eine dritte Art von Benutzer benötige, vielleicht einen Moderator oder etwas Ähnliches. In diesem Fall füge ich ein UserType-Modell (und die entsprechende Migration) hinzu, und eine zweite Migration zum Entfernen des "admin"-Flags aus der Benutzertabelle. Und hier kommt das Problem. In der "add_user_type_to_users"-Migration muss ich den Wert des "admin"-Flags einem Benutzertyp zuordnen. Dazu müssen die Benutzertypen existieren, d. h. ich kann nicht die Seeds-Datei verwenden, sondern muss die Benutzertypen in der Migration erstellen (was ebenfalls als schlechte Praxis gilt). Hier kommt ein fiktiver Code, der die Situation darstellt:
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
Wie Sie sehen können, gibt es zwei problematische Teile. Erstens die Erstellung der Zeile im ersten Modell, die notwendig ist, wenn ich alle Migrationen in einer Reihe durchführen möchte, und zweitens der "Update"-Teil in der zweiten Migration, der die Spalte "admin" der Spalte "type_id" zuordnet.
Haben Sie einen Rat?