Я почти обновил свое приложение RoR с версии 4 до версии 5, и осталось сделать еще несколько вещей, чтобы завершить процесс.
В моем приложении RoR v4 я использовал атрибуты, которые были serialize
d как Hash и Array:
class ModelOne < ApplicationRecord
serialize :attribute_one_names, Hash
end
class ModelTwo < ApplicationRecord
serialize :attribute_two_names, Array
end
Теперь мне нужно обновить записи в базе данных, чтобы они соответствовали новым требованиям RoR v5.
На основе этого ответа Я могу успешно перенести данные 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
Фактически, при выполнении этой миграции значения --- []
сохраняются в базе данных независимо от существующих данных, сериализованных как массив. То есть значение --- []
сохраняется для каждой записи независимо от ранее существовавших данных в базе данных.
Как я могу решить проблему?
Примечание.
Перед запуском 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
YAML.load
, преобразовать вещи по мере необходимости,to_yaml
их вручную и поместить обратно. Все это с использованием низкоуровневого доступа к базе данных. И убедитесь, что вы всегда пропускаете простые массивы и хэши черезserialize
(или лучше забудьте, чтоserialize
существует, и используйте что-то разумное, например столбец JSON или отдельные таблицы). Дайте мне знать, если я далеко от базы dup-hammer. - person mu is too short   schedule 07.01.2019ActionController::Parameters
иActiveSupport::HashWithIndifferentAccess
, вам нужно будет распаковать внешний массив, а затем преобразовать каждый элемент внутри массива в простой старый хэш. Возможно, вarray_of_names << name.to_unsafe_h.to_h
что-то идет не так. Тьфу,serialize
это дьявол. - person mu is too short   schedule 07.01.2019m.attribute_two_names.each ...
наarray_of_names = m.attribute_two_names.map(&:as_json)
(т.е. используйте#as_json
для преобразования всего в простые массивы и хэши, а не пытайтесь сделать это с#to_unsafe_h
и#to_h
). И почему вы используете базу данных? - person mu is too short   schedule 07.01.2019array_of_names = m.attribute_two_names.map(&:as_json)
, как вы написали, но я все еще получаю базу данных, заполненную всеми значениями--- []
. - person Backo   schedule 07.01.2019serialize :attribute_two_names
изModelTwo
, а затем скажетеm.attribute_two_names = array_of_names.to_yaml
(т. е. сами разберетесь с YAML-изацией)? Поддерживает ли ваша версия MySQL столбцы JSON? Возможно, самое время заменитьserialize
чем-то вменяемым. - person mu is too short   schedule 08.01.2019m.attribute_two_names = YAML.load(m.attribute_two_names).as_json.to_yaml; m.save!
. - person mu is too short   schedule 08.01.2019m.attribute_two_names = array_of_names.to_yaml
, я получаю неожиданный токен ошибки 751 в '--- []. - person Backo   schedule 12.01.2019serialize :attribute_two_names
? Я предлагаю вам обрабатывать столбец как текст при миграции и вручную обрабатывать YAML. - person mu is too short   schedule 12.01.2019attribute_two_names
: должен был быть массивом, но был строкой. -- --- []\n - person Backo   schedule 12.01.2019ModelTwo
изapp/models/model_two.rb
, а не ту, которую вы определяете в своей миграции. - person mu is too short   schedule 12.01.2019