6 Stimmen

Aktualisierungslogik in Ihre Migrationen einbauen

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?

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X