version: Rails 4.1
# 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。