Помещение логики обновления в миграциях

Пару раз я был в ситуации, где я хотел осуществить рефакторинг дизайн некоторой модели и закончил тем, что поместил логику обновления в миграции. Однако насколько я понял, это не хорошая практика (тем более, что Вы поощряетесь использовать свой файл схемы для развертывания и не свои миграции). Как Вы имеете дело с подобными проблемами?

К clearify, что я имею в виду, скажите, что у меня есть модель User. Так как я думал, что только будет два вида пользователей, а именно, "нормального" пользователя и администратора, я принял решение использовать простое булево поле, говорящее, был ли пользователь adminstrator или нет.

Однако после того, как я, в то время как я полагал, что мне был нужен некоторый третий вид пользователя, возможно, модератор или что-то подобное. В этом случае я добавляю модель UserType (и соответствующая миграция) и вторая миграция для удаления "администраторского" флага от пользовательской таблицы. И здесь прибывает проблема. В "add_user_type_to_users" миграции я должен отобразить администраторское флаговое значение на пользовательский тип. Кроме того, чтобы сделать это, пользовательские типы должны существовать, означая, что я не могу использовать файл семян, а скорее создать пользовательские типы в миграции (также рассмотренный плохой практикой). Здесь прибывает некоторый вымышленный код, представляющий ситуацию:

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

Поскольку Вы видите, что существует две проблематичных части. Сначала часть создания строки в первой модели, которая необходима, если я хотел бы выполнить все миграции подряд, затем часть "обновления" во второй миграции, которая отображает "администраторский" столбец на "type_id" столбец.

Совет?

6
задан Jon Seigel 23 May 2010 в 01:24
поделиться

2 ответа

Я нахожу более "нетрадиционным" то, что вы используете fk's, чем то, что вы загружаете UserType со старым User.admin, что, я полагаю, происходит довольно часто.

Если вы используете fk's, вы получаете уродливые ошибки mysql, которые путают пользователя. Если же вы используете AR валидации и хуки для обеспечения ссылочной целостности, вы получаете красивые и хорошо интегрированные сообщения об ошибках, которые не нарушают пользовательский поток вашего приложения.

Не беспокойтесь о миграции, которая будет выполнена один раз, и подумайте о бизнес-логике, которую вы выносите за пределы своего кода.

Это все мнения/обсуждения, но я надеюсь, что мои мысли будут вам полезны.

1
ответ дан 17 December 2019 в 22:11
поделиться

Обычно для этой цели используется файл db / seed.rb - записи, помещенные в него, будут загружены как часть rake db: setup

Тем не менее, я всегда обнаруживал, что рельсы не справляются с этой проблемой. Я думал о написании плагина, который дает вам папку db / seed, имеет исходные файлы с отметкой даты для добавления записей (возможно .yml) и отслеживает исходные данные в системной таблице, чтобы их можно было вернуть / обновить.

0
ответ дан 17 December 2019 в 22:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: