Почему я получаю эту ошибку "не могу модифицировать замороженную хэш"?
У меня есть модель Person и модель Item. У человека много предметов, и предмет принадлежит человеку.
В этом коде мне нужно удалить существующие элементы для человека и создать новые из параметра (который представляет собой массив хэшей). Затем мне нужно обновить одно из полей элемента, основанное на одном из его других полей.
@person = Person.find(params["id"])
@person.person_items.each do |q|
q.destroy
end
person_items_from_param = ActiveSupport::JSON.decode(params["person_items"])
person_items_from_param.each do |pi|
@person.person_items.create(pi) if pi.is_a?(Hash)
end
@person.person_items.each do |x|
if x.item_type == "Type1"
x.item_amount = "5"
elsif x.item_type == "Type2"
x.item_amount = "10"
end
x.save
end
В строках x.item_amount = "5"
и x.item_amount = "10"
я получаю эту ошибку:
RuntimeError in PersonsController#submit_items
can't modify frozen hash
Как я могу это исправить? Спасибо за чтение.
Ответы
Ответ 1
Я бы заподозрил
ActiveSupport::JSON.decode(params["person_items"])
возвращает замороженный хеш, который затем используется для создания объектов
@person.person_items.create(pi) if pi.is_a?(Hash)
И так как он заблокирован, вы не можете его изменить.
Вы могли
а
Сделайте глубокую копию объекта JSON
или
В
Перезагрузите экземпляр модели, который должен восстановить объект, делающий поля незамеренными.
Вариант A является "лучшим" решением, но сложным, потому что единственный способ, которым я знаю глубокое копирование, - сериализация, десериализация и создание объекта и назначение возвращаемого значения.
Ответ 2
Если вы используете q.destroy перед сохранением элемента, вы получите ошибку. лучше сначала сохраните элемент, а затем используйте destroy.
Ответ 3
Вы можете обойти это, если вы снова прочитаете person_items из базы данных, а не используете ассоциацию. Связь устарела и указывает на уничтоженные строки.
Вместо
@person.person_items.each do |x|
Try
PersonItem.where(:person_id=>@person.id).each do |x|
Ответ 4
Вы можете сделать глубокую копию любого объекта в рельсах, включая JSON, так что просто сделайте это.
Помните, что clone
сохраняет замороженное состояние, а dup
- нет.
Самый простой способ исправить ошибку can't modify frozen Array
- это dup
этот замороженный массив;)
person_items_from_param = ActiveSupport::JSON.decode(params["person_items"]).dup