Почему я получаю сообщение об ошибке «невозможно изменить замороженный хэш»? - PullRequest
11 голосов
/ 19 декабря 2010

У меня есть модель 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 

Как я могу это исправить?Спасибо за чтение.

Ответы [ 4 ]

7 голосов
/ 19 декабря 2010

Я бы заподозрил

ActiveSupport::JSON.decode(params["person_items"])

возвращает замороженный хеш, который вы затем используете для создания объектов

@person.person_items.create(pi) if pi.is_a?(Hash)

А поскольку он заморожен, его нельзя изменить.

Вы могли бы

А Сделайте глубокую копию объекта JSON

или

B Перезагрузите экземпляр модели, который должен восстановить объект, делая поля незамерзшими.

Вариант A является «лучшим» решением, но трудным, потому что я знаю только один способ глубокого копирования - сериализацию и десериализацию, создание объекта и назначение возвращаемого значения.

6 голосов
/ 11 апреля 2014

Если вы используете q.destroy перед сохранением элемента, вы получите ошибку.лучше сначала сохранить элемент, а затем использовать команду destroy.

2 голосов
/ 19 августа 2013

Вы можете обойти это, если снова прочитаете person_items из базы данных, а не используете связь. Ассоциация устарела и указывает на уничтоженные строки.

вместо @person.person_items.each do |x|

Try PersonItem.where(:person_id=>@person.id).each do |x|

0 голосов
/ 15 января 2018

Вы можете сделать глубокую копию любого объекта в рельсах, включая JSON, так что просто сделайте это. Помните, что clone сохраняет замороженное состояние, а dup - нет.

Самый простой способ исправить ошибку can't modify frozen Array это dup этот замороженный массив;)

person_items_from_param = ActiveSupport::JSON.decode(params["person_items"]).dup
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...