将更新逻辑放入迁移中

时间:2010-05-22 07:08:35

标签: ruby-on-rails migration rails-migrations

有几次我一直处于这种情况,我想重构一些模型的设计,并最终将更新逻辑放入迁移中。但是,据我所知,这不是一种好的做法(特别是因为鼓励您使用模式文件进行部署,而不是迁移)。你是如何处理这类问题的?

要清楚我的意思,请说我有一个用户模型。由于我认为只有两种用户,即“普通”用户和管理员,我选择使用一个简单的布尔字段来判断用户是否是管理员。

然而,在我想到我需要第三类用户之后,可能是主持人或类似的东西。在这种情况下,我添加了一个UserType模型(和相应的迁移),以及第二次迁移,用于从用户表中删除“admin”标志。这就是问题所在。在“add_user_type_to_users”迁移中,我必须将admin标志值映射到用户类型。此外,为了做到这一点,用户类型必须存在,这意味着我不能使用种子文件,而是在迁移中创建用户类型(也被认为是不好的做法)。这里有一些代表情况的虚构代码:

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”列。 / p>

有什么建议吗?

2 个答案:

答案 0 :(得分:1)

我发现你使用fk比使用旧的User.admin加载UserType更加'非常规',我猜这种情况经常发生。

如果你使用fk,你会得到丑陋的mysql错误,这会让用户感到困惑。否则,您使用AR验证和挂钩来强制执行参照完整性,您将获得非常好且整合良好的错误消息,这些消息不会破坏您应用的用户体验流。

不要担心会运行一次的迁移并考虑您放在代码之外的业务逻辑。

这是所有意见/惯例的问题,但我希望你发现我的见解有些帮助。

答案 1 :(得分:0)

db / seeds.rb文件通常用于此目的 - 放在其中的记录将作为其中一部分加载 rake db:setup

然而,我总是发现铁轨在这个问题上出现了问题。 我一直在考虑编写一个插件,它给你一个db / seeds文件夹,有一个用于添加记录的日期戳文件(也许是.yml),并跟踪系统表中的种子数据,以便可以恢复/更新。