特定数据库连接上的 ActiveRecord 迁移无法回滚
ActiveRecord migration on specific database connection can't rollback
TL;DR - 在迁移中使用显式数据库连接会破坏可逆性。
我有一个简单的迁移,将一列添加到特定的数据库连接:(我的应用程序有几个数据库,这是有充分理由的)
class AddFlavorToBikes < ActiveRecord::Migration
def change
VehicleBase.connection.tap do |db|
db.add_column :bikes, :flavor, :string
end
end
end
效果很好:
% be rake db:migrate
== 20181120215337 AddFlavorToBikes: migrating =================================
== 20181120215337 AddFlavorToBikes: migrated (0.0060s) ========================
但是回滚失败:
% be rake db:rollback
== 20181120215337 AddFlavorToBikes: reverting =================================
rake aborted!
An error has occurred, this and all later migrations canceled:
PG::DuplicateColumn: ERROR: column "flavor" of relation "bikes" already exists
: ALTER TABLE "bikes" ADD "flavor" character varying/Users/david/rider-gate/db/migrate/20181120215337_add_flavor_to_bikes.rb:5:in `block in change'
/Users/david/rider-gate/db/migrate/20181120215337_add_flavor_to_bikes.rb:3:in `tap'
/Users/david/rider-gate/db/migrate/20181120215337_add_flavor_to_bikes.rb:3:in `change'
我不明白这个。 db.add_column 的回滚应该 删除 列。那么,为什么我会收到一条错误消息,提示我要删除的字段已经存在? 当然它存在,这就是我试图将其删除的原因。
我在互联网上搜索了解决方案,甚至搜索了任何有相同问题的人,但没有找到任何线索。
我尝试使用显式变量而不是 .tap,但得到了同样的错误:
class AddFlavorToBikes < ActiveRecord::Migration
def change
db = VehicleBase.connection
db.add_column :bikes, :flavor, :string
end
end
我能辨别的最接近的,ActiveRecord::Migration 失去了检测它是否在除默认 ActiveRecord::Base 连接之外的任何连接上向上或向下迁移的能力。
因此,它尝试向上迁移 add_column
,即使它处于回滚状态并且应该向下迁移。因此,它试图第二次添加该列,而不是将 add_column 反转为 remove_column.
这是 Rails 4.2.7 和 Ruby 2.1.9
如何使此迁移可逆?
我通过将 change
拆分为 up
和 down
方法找到了一个合理的解决方案:
class AddFlavorToBikes < ActiveRecord::Migration
def up
VehicleBase.connection.tap do |db|
db.add_column :bikes, :flavor, :string
end
end
def down
VehicleBase.connection.tap do |db|
db.remove_column :bikes, :flavor
end
end
end
虽然不像可逆迁移那样优雅或 DRY,但这允许 db:migrate 和 db:rollback 成功工作。
TL;DR - 在迁移中使用显式数据库连接会破坏可逆性。
我有一个简单的迁移,将一列添加到特定的数据库连接:(我的应用程序有几个数据库,这是有充分理由的)
class AddFlavorToBikes < ActiveRecord::Migration
def change
VehicleBase.connection.tap do |db|
db.add_column :bikes, :flavor, :string
end
end
end
效果很好:
% be rake db:migrate
== 20181120215337 AddFlavorToBikes: migrating =================================
== 20181120215337 AddFlavorToBikes: migrated (0.0060s) ========================
但是回滚失败:
% be rake db:rollback
== 20181120215337 AddFlavorToBikes: reverting =================================
rake aborted!
An error has occurred, this and all later migrations canceled:
PG::DuplicateColumn: ERROR: column "flavor" of relation "bikes" already exists
: ALTER TABLE "bikes" ADD "flavor" character varying/Users/david/rider-gate/db/migrate/20181120215337_add_flavor_to_bikes.rb:5:in `block in change'
/Users/david/rider-gate/db/migrate/20181120215337_add_flavor_to_bikes.rb:3:in `tap'
/Users/david/rider-gate/db/migrate/20181120215337_add_flavor_to_bikes.rb:3:in `change'
我不明白这个。 db.add_column 的回滚应该 删除 列。那么,为什么我会收到一条错误消息,提示我要删除的字段已经存在? 当然它存在,这就是我试图将其删除的原因。
我在互联网上搜索了解决方案,甚至搜索了任何有相同问题的人,但没有找到任何线索。
我尝试使用显式变量而不是 .tap,但得到了同样的错误:
class AddFlavorToBikes < ActiveRecord::Migration
def change
db = VehicleBase.connection
db.add_column :bikes, :flavor, :string
end
end
我能辨别的最接近的,ActiveRecord::Migration 失去了检测它是否在除默认 ActiveRecord::Base 连接之外的任何连接上向上或向下迁移的能力。
因此,它尝试向上迁移 add_column
,即使它处于回滚状态并且应该向下迁移。因此,它试图第二次添加该列,而不是将 add_column 反转为 remove_column.
这是 Rails 4.2.7 和 Ruby 2.1.9
如何使此迁移可逆?
我通过将 change
拆分为 up
和 down
方法找到了一个合理的解决方案:
class AddFlavorToBikes < ActiveRecord::Migration
def up
VehicleBase.connection.tap do |db|
db.add_column :bikes, :flavor, :string
end
end
def down
VehicleBase.connection.tap do |db|
db.remove_column :bikes, :flavor
end
end
end
虽然不像可逆迁移那样优雅或 DRY,但这允许 db:migrate 和 db:rollback 成功工作。