处理 heroku 上不存在的 table 的 drop_table 迁移
Deal with a drop_table migration for nonexistent table on heroku
我在本地环境的 postgres 数据库中创建了一个 'campaigns' table。我从未提交更改,因此从未将其推送到 heroku 上。
当我改变主意需要 table 时,我愚蠢地手动删除了迁移(我现在知道永远不要这样做)。但是,由于迁移已经有 运行,我创建了一个 21041211003219_drop_campaigns_table
迁移以从本地数据库中删除 table。
现在当我 运行 heroku run rake db:migrate
生产时,我得到以下错误:
Migrating to DropCampaignsTable (20141211003219)
== 20141211003219 DropCampaignsTable: migrating ===============================
-- drop_table(:campaigns)
PG::UndefinedTable: ERROR: table "campaigns" does not exist
: DROP TABLE "campaigns"
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:
PG::UndefinedTable: ERROR: table "campaigns" does not exist
: DROP TABLE "campaigns"/app/vendor/bundle/ruby/2.0.0/gems/activerecord-4.1.0/lib/active_record/connection_adapters/postgresql/database_statements.rb:128:in `async_exec'
/app/vendor/bundle/ruby/2.0.0/gems/activerecord-4.1.0/lib/active_record/connection_adapters/postgresql/database_statements.rb:128:in `block in execute'
我知道它正在尝试 运行 删除 table 迁移到一个它从来不知道存在的 table 但我不确定如何处理这个。
我尝试删除 drop table 迁移,将其推送到生产环境并 运行ning 它,但它仍然尝试 运行 该迁移。
回滚该提交后,我一直在考虑在重新创建 table 的 21041211003219_drop_campaigns_table
迁移之前手动创建一个带有日期戳的新迁移,但后来我觉得我'将留下本地 postgres 数据库中存在的 table(因为 21041211003219_drop_campaigns_table
已经在本地 运行)。
我是否继续手动创建然后尝试重新运行 删除迁移?
有人可以告诉我最好的行动方案吗?
只需删除有问题的迁移并继续做更有趣的事情。显然 table 没有 "create table" 迁移,因此 "drop table" 迁移无权存在。如果实在不想删除migration,可以使用raw SQL:
重写
def up
connection.execute 'drop table if exists campaigns'
end
if exists
完全符合您的想法:它只会尝试删除 table(如果存在)。
关于这个东西:
I thought you weren't supposed to delete migrations so that anyone else that works on the project in the future can run them and have an accurate DB.
迁移旨在将应用程序的现有实例从状态 A 转移到状态 B。如果您要启动一个新实例,您将使用 schema.rb
(或 structure.sql
)来初始化应用程序并且不需要迁移。此外,一旦将迁移应用于应用程序的每个实例,除非您想尝试倒退,否则实际上不再需要迁移。我倾向于在发布后几周清除旧的迁移以保持混乱。
删除已经不存在的 table 时,您可以关闭错误 PG::UndefinedTable: ERROR: table "campaigns" does not exist
,方法是将 if_exists: true
添加到 drop_table
:
def up
drop_table(:campaigns, if_exists: true)
end
此选项的文档很少,但存在于 Rails since early 2015 中,我认为它比手写 SQL 更干净,因为它还处理与各种数据库适配器的兼容性。
普通SQL
def down
execute <<~SQL
DROP TABLE IF EXISTS campaigns;
SQL
end
if_exists
不适用于 rails 4.2 和 PG 9.6。感谢@MartinSommer 提到 table_exists?
。对于后代,这是我使用的语法,读起来更清晰。
drop_table :campaigns if table_exists?(:campaigns)
当您删除迁移文件并希望删除相应的 table 时,例如student_items
,有两种方式:
最简单的方法:将迁移文件恢复到存档,然后运行
rails db:rollback
创建迁移:
rails g migration DropStudentItemTable
并在迁移文件中添加此代码:
class DropStudentItemTables < ActiveRecord::Migration[6.0]
def down
table_exists?(:student_items) ? drop_table(:student_items) : nil
end
end
我在本地环境的 postgres 数据库中创建了一个 'campaigns' table。我从未提交更改,因此从未将其推送到 heroku 上。
当我改变主意需要 table 时,我愚蠢地手动删除了迁移(我现在知道永远不要这样做)。但是,由于迁移已经有 运行,我创建了一个 21041211003219_drop_campaigns_table
迁移以从本地数据库中删除 table。
现在当我 运行 heroku run rake db:migrate
生产时,我得到以下错误:
Migrating to DropCampaignsTable (20141211003219)
== 20141211003219 DropCampaignsTable: migrating ===============================
-- drop_table(:campaigns)
PG::UndefinedTable: ERROR: table "campaigns" does not exist
: DROP TABLE "campaigns"
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:
PG::UndefinedTable: ERROR: table "campaigns" does not exist
: DROP TABLE "campaigns"/app/vendor/bundle/ruby/2.0.0/gems/activerecord-4.1.0/lib/active_record/connection_adapters/postgresql/database_statements.rb:128:in `async_exec'
/app/vendor/bundle/ruby/2.0.0/gems/activerecord-4.1.0/lib/active_record/connection_adapters/postgresql/database_statements.rb:128:in `block in execute'
我知道它正在尝试 运行 删除 table 迁移到一个它从来不知道存在的 table 但我不确定如何处理这个。
我尝试删除 drop table 迁移,将其推送到生产环境并 运行ning 它,但它仍然尝试 运行 该迁移。
回滚该提交后,我一直在考虑在重新创建 table 的 21041211003219_drop_campaigns_table
迁移之前手动创建一个带有日期戳的新迁移,但后来我觉得我'将留下本地 postgres 数据库中存在的 table(因为 21041211003219_drop_campaigns_table
已经在本地 运行)。
我是否继续手动创建然后尝试重新运行 删除迁移?
有人可以告诉我最好的行动方案吗?
只需删除有问题的迁移并继续做更有趣的事情。显然 table 没有 "create table" 迁移,因此 "drop table" 迁移无权存在。如果实在不想删除migration,可以使用raw SQL:
重写def up
connection.execute 'drop table if exists campaigns'
end
if exists
完全符合您的想法:它只会尝试删除 table(如果存在)。
关于这个东西:
I thought you weren't supposed to delete migrations so that anyone else that works on the project in the future can run them and have an accurate DB.
迁移旨在将应用程序的现有实例从状态 A 转移到状态 B。如果您要启动一个新实例,您将使用 schema.rb
(或 structure.sql
)来初始化应用程序并且不需要迁移。此外,一旦将迁移应用于应用程序的每个实例,除非您想尝试倒退,否则实际上不再需要迁移。我倾向于在发布后几周清除旧的迁移以保持混乱。
删除已经不存在的 table 时,您可以关闭错误 PG::UndefinedTable: ERROR: table "campaigns" does not exist
,方法是将 if_exists: true
添加到 drop_table
:
def up
drop_table(:campaigns, if_exists: true)
end
此选项的文档很少,但存在于 Rails since early 2015 中,我认为它比手写 SQL 更干净,因为它还处理与各种数据库适配器的兼容性。
普通SQL
def down
execute <<~SQL
DROP TABLE IF EXISTS campaigns;
SQL
end
if_exists
不适用于 rails 4.2 和 PG 9.6。感谢@MartinSommer 提到 table_exists?
。对于后代,这是我使用的语法,读起来更清晰。
drop_table :campaigns if table_exists?(:campaigns)
当您删除迁移文件并希望删除相应的 table 时,例如student_items
,有两种方式:
最简单的方法:将迁移文件恢复到存档,然后运行
rails db:rollback
创建迁移:
rails g migration DropStudentItemTable
并在迁移文件中添加此代码:
class DropStudentItemTables < ActiveRecord::Migration[6.0] def down table_exists?(:student_items) ? drop_table(:student_items) : nil end end