处理 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,有两种方式:

  1. 最简单的方法:将迁移文件恢复到存档,然后运行

    rails db:rollback
    
  2. 创建迁移:

    rails g migration DropStudentItemTable
    

    并在迁移文件中添加此代码:

    class DropStudentItemTables < ActiveRecord::Migration[6.0]
      def down
        table_exists?(:student_items) ? drop_table(:student_items) : nil
      end
    end