【问题标题】:Putting update logic in your migrations将更新逻辑放入迁移中
【发布时间】:2010-05-24 07:41:50
【问题描述】:

有几次我想重构某些模型的设计,并最终将更新逻辑放入迁移中。但是,据我了解,这不是一个好的做法(特别是因为我们鼓励您使用架构文件进行部署,而不是迁移)。您如何处理这些问题?

为了澄清我的意思,假设我有一个用户模型。因为我认为只有两种用户,即“普通”用户和管理员,所以我选择使用一个简单的布尔字段来判断用户是否是管理员。

然而,在我认为我需要第三类用户之后,也许是版主或类似的人。在这种情况下,我添加了一个 UserType 模型(和相应的迁移),以及用于从用户表中删除“admin”标志的第二个迁移。问题来了。在“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

如您所见,有两个有问题的部分。首先是第一个模型中的行创建部分,如果我想连续运行所有迁移,这是必要的,然后是第二个迁移中的“更新”部分,它将“admin”列映射到“type_id”列。

有什么建议吗?

【问题讨论】:

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


    【解决方案1】:

    我发现使用 fk 比使用旧的 User.admin 加载 UserType 更“非传统”,我猜这种情况经常发生。

    如果你使用 fk,你会得到让用户感到困惑的难看的 mysql 错误。否则,您使用 AR 验证和挂钩来强制执行引用完整性,您会收到漂亮且集成良好的错误消息,不会破坏您应用的用户体验流程。

    不必担心将运行一次的迁移,并考虑将业务逻辑置于代码之外。

    这完全是意见/惯例问题,但我希望我的见解对您有所帮助。

    【讨论】:

    • 当涉及到外键时,我发现它们非常有用,尤其是在处理业务关键数据时。它可以帮助我避免悬空数据,并检测我是否错过了从 Rails 方面应用钩子等。虽然你是对的,但它们可能会给你一些丑陋的错误信息。
    • 我明白了,这是一个选择问题。如果备份之外的数据完整性超过了用户体验,那么 fk 是要走的路。在这种情况下,您有一个用于管理迁移中 fk 的插件:agilewebdevelopment.com/plugins/foreign_key_migrations
    【解决方案2】:

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

    但是,我总是发现 rails 会解决这个问题。 我一直在考虑编写一个插件,它为您提供一个 db/seeds 文件夹,具有用于添加记录的带日期戳的种子文件(可能是 .yml)并跟踪系统表中的种子数据,以便可以恢复/更新。

    【讨论】:

    • 从代码中可以看出,我不能使用种子,因为我想一次应用多个迁移,最后一次迁移取决于数据库中有数据。如果每次迁移都有一个种子文件,并且种子文件将在迁移之间应用,那就太好了。因此,首先运行 20100524...do_something.rb,然后 20100524...seed_something.rb 执行此迁移所需的播种。
    猜你喜欢
    • 1970-01-01
    • 2014-05-06
    • 1970-01-01
    • 1970-01-01
    • 2023-03-11
    • 2013-11-29
    • 2021-06-22
    • 1970-01-01
    • 2011-08-21
    相关资源
    最近更新 更多