`
jiajie0531
  • 浏览: 27536 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Rails Database Migrations 6 Using Models in Your Migrations 在你的数据迁移中使用模型

阅读更多

version: Rails 4.1

当在一个数据迁移文件(migration)里创建或者修改数据时, 通常比较吸引人的做法是使用你的模型中的一个. 毕竟, 他们的存在是为了提供更便捷的读取那些潜在的数据. 这是能够实现的, 但有些警告应该要被注意到.
 
例如, 问题的产生是在模型使用数据列时,1)这些数据列没有存在于数据库中 2)将会被这个或者随后的 migration 来创建。
 
考虑这个例子,Alice 和 Bob 共同工作于同一个代码,包含一个 Product 模型:
 
Bob 度假去了。
 
Alice 创建了一个 数据迁移文件(migration) 服务于 products 数据表,增加了一个新的列,然后初始化它:

# db/migrate/20100513121110_add_flag_to_product.rb
 
class AddFlagToProduct < ActiveRecord::Migration
  def change
    add_column :products, :flag, :boolean
    reversible do |dir|
      dir.up { Product.update_all flag: false }
    end
  end
end

她同样地也给 Product的一个新的列 增加了一个验证:


# app/models/product.rb
 
class Product < ActiveRecord::Base
  validates :flag, inclusion: { in: [true, false] }
end

Alice 增加第二个 migration,用来在 products 数据表中增加另一个列,然后再初始化它:


# db/migrate/20100515121110_add_fuzz_to_product.rb
 
class AddFuzzToProduct < ActiveRecord::Migration
  def change
    add_column :products, :fuzz, :string
    reversible do |dir|
      dir.up { Product.update_all fuzz: 'fuzzy' }
    end
  end
end

她同样地也对于 Product 模型的一个新的列增加了一个验证:


# app/models/product.rb
 
class Product < ActiveRecord::Base
  validates :flag, inclusion: { in: [true, false] }
  validates :fuzz, presence: true
end

两个 migrations 都是为 Alice 工作的。

Bob 从假期中回来了:

  • 更新了代码—— 这其中包含了两个 migrations 和最近版本的 Product 模型。
  • 运行未被执行过的 migrations 用 rake db:migrate,包括那个 Product 模型的更新。

migration 有冲突了,因为当模型想要保存的时候,尝试着去验证第二个新增的列,当第一个 migration 运行的时候,这一列是不存在与数据库中的:


rake aborted!
An error has occurred, this and all later migrations canceled:
 
undefined method `fuzz' for #<Product:0x000001049b14a0>

一个解决的办法就是,在一个 migration 中创建一个本地模型。这使得让 Rails 持续地执行验证,因此 migrations 可以完整的运行了。

当使用一个本地模型时,比较好的做法是调用 Product.reset_column_information 来刷新 Active Record 的缓存,为了 Product 模型在数据库中优先更新数据。

如果 Alice 已经这样做了,那就不会存在问题了:

# db/migrate/20100513121110_add_flag_to_product.rb
 
class AddFlagToProduct < ActiveRecord::Migration
  class Product < ActiveRecord::Base
  end
 
  def change
    add_column :products, :flag, :boolean
    Product.reset_column_information
    reversible do |dir|
      dir.up { Product.update_all flag: false }
    end
  end
end
# db/migrate/20100515121110_add_fuzz_to_product.rb
 
class AddFuzzToProduct < ActiveRecord::Migration
  class Product < ActiveRecord::Base
  end
 
  def change
    add_column :products, :fuzz, :string
    Product.reset_column_information
    reversible do |dir|
      dir.up { Product.update_all fuzz: 'fuzzy' }
    end
  end
end

在上面的例子中,可能还有其他的错误。

 

例如,想象一下,Alice 创建了一个 migration ,有选择地更新某些 products 的 description 字段。她运行 migration,提交代码,然后开始工作于下一个任务,在 products 数据表中增加一个新的列。

 

对于这个新的任务,她创建了两个 migrations,一方面是增加了新的列,另一方面是有选择地更新 fuzz 列,基于 product 的其他特性。

 

这些 migrations 可以很好地执行,但当 Bob 从他的假期中归来,调用 rake db:migrate 去运行所有未执行过的 migrations,他得到了一个不可思议的 bug :description 字段中有默认值,fuzz 列是显示的,但 所有的products中 fuzz 的值是nil。

 
解决的办法是再一次使用 Product.reset_column_information, 位于一个 migration 里引用 Product 模型之前。在操作那些记录数据之前,确保 Active Record 能了解当前其数据表结构。
 

 

0
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics