运行 在我的 Rails 数据库上迁移,悄悄地添加一列并随机删除 table 而没有控制台错误

Running migrations on my Rails DB, adding a column quietly and randomly drops a table without a console error

我只能通过慢慢浏览 运行 我的迁移才能发现这个问题。在过去的两天里,我一直在尝试做一些简单的事情。从我的 simulations table 中删除一列 verdict:text 并添加另一列:opinions:hash

这样做会导致各种错误,例如没有方法 'my_sym' 和说模拟 table 不存在。

我最初以为我已经解决了这个问题:

rake db:drop
rake db:schema:dump
rake db:migrate VERSION="<Migration1>"
rake db:migrate VERSION="<Migration2>"
rake db:setup ENV="test"

迁移 1:

class DeviseCreateUsers < ActiveRecord::Migration
  def change
    create_table(:users) do |t|
      ## Database authenticatable
      t.string :email,              :null => false, :default => "", limit: 96
      t.string :encrypted_password, :null => false, :default => "", limit: 60

      t.timestamps

      t.index :email, unique: true
    end
  end
end

迁移 2:

class CreateSimulations < ActiveRecord::Migration
  def change
    # Needs the hash column worked out before this is run 
    create_table :simulations do |t|
        t.integer :x_size
        t.integer :y_size
        t.string :verdict
        t.string :arrangement 
    end

    add_reference :simulations, :user, index: true    
  end
end

这让我回到原点(我所有的测试都有效,一切似乎都处于我开始出现问题之前的初始状态),所以我重新编写了两个有问题的迁移,认为它们可能是问题所在,运行 按此顺序进行的两次迁移(我删除了该列然后添加了另一列,与我之前尝试的顺序相反,认为该顺序可能有一些影响)。

迁移 3:

class RemoveVerdictFromSimulation < ActiveRecord::Migration
  def change
    remove_column :simulations, :verdict, :string
  end
end

迁移 4:

class AddOpinionToSimulations < ActiveRecord::Migration
  def change
    add_column :simulations, :opinion, :hash
  end
end

编辑:经过进一步调查,正是这第 4 次迁移导致了没有错误地删除模拟 table 的问题。

这两个迁移都没有错误。但是我知道做任何其他事情都会导致错误(例如 运行ning rspec),因为看起来问题是其中一个迁移导致 schema.rb 文件出错。

在 运行完成这些最后的迁移 3 和 4 之后,我的 schema.rb 以前同时拥有用户和模拟 tables 现在看起来像这样:

ActiveRecord::Schema.define(version: 20150807193122) do

# Could not dump table "simulations" because of following NoMethodError
#   undefined method `[]' for nil:NilClass

  create_table "users", force: :cascade do |t|
    t.string   "email",              limit: 96, default: "", null: false
    t.string   "encrypted_password", limit: 60, default: "", null: false
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  add_index "users", ["email"], name: "index_users_on_email", unique: true

end

这里特别感兴趣的是:

# Could not dump table "simulations" because of following NoMethodError
#   undefined method `[]' for nil:NilClass

任何人都可以阐明这里发生的事情或我如何解决它吗?我已经坚持了几个小时。

有一件事可能值得注意,当我重新 运行 rake db:schema:dump 时,出于某种原因我也需要注释掉我的工厂,因为它们似乎会导致模拟错误 table现存的。不知道为什么他们被称为只是认为这个信息可能有帮助,这里他们都是:

FactoryGirl.define do 
    factory :simulation do |f|
        f.id (Simulation.last.nil? ? 1 : Simulation.last.id + 1)
        f.x_size 3
        f.y_size 3
        f.user_id 1
    end 
end 

--

FactoryGirl.define do
    factory :user do
        email "user_#{User.last.nil? ? 1 : User.last.id + 1}@home.com"
        password "password"
    end
end

我认为问题就在这里:

class AddOpinionToSimulations < ActiveRecord::Migration
  def change
    add_column :simulations, :opinion, :hash
  end
end

没有 hash 列类型。不幸的是,SQLite 允许您创建任意类型的列(将无法识别的内容视为字符串),但 ActiveRecord 不知道如何处理它们。结果是您的迁移有效但模式转储失败,因为 ActiveRecord 不能说:

t.hash :opinion

schema.rb 文件中。

如果你真的认为你想在列中存储哈希,你会创建一个 text 列:

add_column :simulations, :opinion, :text

然后在您的模型中使用 serialize

serialize :opinion, Hash

当然,这会在您的数据库中留下一个不透明的 YAML 块,您将无法(理智地)查询它,因此只有在您同意的情况下才应使用它。

更好的解决方案是规范化您的 opinion 哈希,以便您可以将它们的信息存储在单独的 tables/models.