我几乎将我的RoR应用程序从v4升级到了v5,还有许多工作要做以完成该过程。
在我的RoR v4应用程序中,我使用的是serialize
d作为哈希和数组的属性:
class ModelOne < ApplicationRecord
serialize :attribute_one_names, Hash
end
class ModelTwo < ApplicationRecord
serialize :attribute_two_names, Array
end
现在,我需要更新数据库中的记录,以符合新的RoR v5要求。
基于this answer,我可以通过运行以下迁移成功迁移attribute_one_names
(哈希)数据:
class MigrationOneFromRor4ToRor5 < ActiveRecord::Migration[5.2]
class ModelOne < ApplicationRecord
self.table_name = 'model_one'
serialize :attribute_one_names
end
def up
ModelOne.all.each do |m|
h = m.attribute_one_names.to_unsafe_h.to_h
m.attribute_one_names = h
m.save!
end
end
end
问题出在attribute_two_names
(数组)数据上。
class MigrationTwoFromRor4ToRor5 < ActiveRecord::Migration[5.2]
class ModelTwo < ApplicationRecord
self.table_name = 'model_two'
serialize :attribute_two_names
end
def up
ModelTwo.all.each do |m|
array_of_names = []
m.attribute_two_names.each do |name|
array_of_names << name.to_unsafe_h.to_h
end
# Output 1:
puts array_of_names.inspect
# => [{"name"=>"Website1Name", "url"=>"http://www.website1.com"}, {"name"=>"Website2Name", "url"=>"http://www.website2.com"}]
puts m.attribute_two_names.inspect
# => [<ActionController::Parameters {"name"=>"Website1Name", "url"=>"http://www.website1.com"} permitted: false>, <ActionController::Parameters {"name"=>"Website2Name", "url"=>"http://www.website2.com"} permitted: false>]
m.attribute_two_names = array_of_names
# Output 2:
puts m.attribute_two_names.inspect
# => [{"name"=>"Website1Name", "url"=>"http://www.website1.com"}, {"name"=>"Website2Name", "url"=>"http://www.website2.com"}]
m.save!
# Output 3:
puts m.attribute_two_names.inspect
# => []
end
end
end
实际上,通过运行此迁移,无论序列化为Array的现有数据如何,--- []
值都存储在数据库中。也就是说,无论数据库中是否存在先前的数据,都会为每个记录存储一个--- []
值。
我该如何解决问题?
注意:
在运行MigrationTwoFromRor4ToRor5之前,attribute_two_names
数据库列中的值如下:
---
- !ruby/hash:ActionController::Parameters
name: Website1Name
url: http://www.website1.com
- !ruby/hash:ActionController::Parameters
name: Website2Name
url: http://www.website2.com
---
- !map:ActiveSupport::HashWithIndifferentAccess
name: Website1Name
url: http://www.website1.com
- !map:ActiveSupport::HashWithIndifferentAccess
name: Website2Name
url: http://www.website2.com
答案 0 :(得分:0)
您可以尝试手动解压缩YAML以获得原始的旧哈希和数组,然后手动重新对该数据进行YAML。像这样:
class MigrationTwoFromRor4ToRor5 < ActiveRecord::Migration[5.2]
# Make sure this name is not used by any real models
# as we want our own completely separate interface to
# the `model_two` table.
class ModelTwoForMigrationChicanery < ApplicationRecord
self.table_name = 'model_two'
# We'll be treating `attribute_two_names` as just
# a blob of text in here so no `serialize`.
end
def up
ModelTwoForMigrationChicanery.all.each do |m|
# Unpack the YAML that `serialize` uses.
a = YAML.load(m.attribute_two_names)
# Using `as_json` is a convenient way to unwrap
# all the `ActionController::Parameters` and
# `ActiveSupport::HashWithIndifferentAccess`
# noise that go into the database. `#as_json`
# on both of those give you plain old hashes.
a = a.as_json
# Manually YAMLize the cleaned up data.
m.attribute_two_names = a.to_yaml
# And put it back in the database.
m.save!
end
end
end
一旦所有这些问题都解决了,您应该在模型和控制器中添加一些内容,以确保只有简单的数组和散列能够传递到serialize
。我还建议您从serialize
移到类似JSON列类型(如果您的数据库支持它,而ActiveRecord支持您的数据库支持)之类的东西。