运行 在我的 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.
中
我只能通过慢慢浏览 运行 我的迁移才能发现这个问题。在过去的两天里,我一直在尝试做一些简单的事情。从我的 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.