Внедрение логики обновления в ваши миграции - PullRequest
6 голосов
/ 22 мая 2010

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

Чтобы прояснить, что я имею в виду, скажем, у меня есть модель пользователя. Поскольку я думал, что будет только два типа пользователей, а именно «обычный» пользователь и администратор, я решил использовать простое логическое поле, указывающее, был ли пользователь администратором или нет.

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

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

Как видите, есть две проблемные части. Сначала часть создания строки в первой модели, которая необходима, если я хочу выполнить все миграции подряд, затем часть обновления во второй миграции, которая отображает столбец «admin» в столбец «type_id».

Любой совет?

Ответы [ 2 ]

1 голос
/ 22 мая 2010

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

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

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

Это все вопрос мнения / соглашения, но я надеюсь, что вы найдете мои идеи полезными.

0 голосов
/ 24 мая 2010

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

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

...