为什么我得到这个'无法修改冻结哈希'错误?

时间:2010-12-19 05:42:37

标签: ruby-on-rails ruby

我有一个Person模型&物品模型。一个人有很多项目,一个项目属于一个人。

在这段代码中,我需要删除一个人的现有项目,并从参数(一个哈希数组)中创建新的项目。然后,我需要根据其他一个字段更新项目的一个字段。

@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 个答案:

答案 0 :(得分:7)

我怀疑

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

返回一个冻结的哈希值,然后用它来创建对象

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

自冻结后你无法修改它。

你可以

一个 制作JSON对象的深层副本

乙 重新加载模型实例,该实例应该重新实现对象,使字段解冻。

选项A是“更好”的解决方案,但很难,因为我知道深度复制的唯一方法是序列化和反序列化以及对象并分配返回值。

答案 1 :(得分:6)

如果在保存元素之前使用q.destroy,则会收到错误。最好先保存元素然后再使用destroy。

答案 2 :(得分:2)

如果您再次从数据库中读取person_items而不是使用关联,则可以解决此问题。该关联是陈旧的,并指向被破坏的行。

而不是 @person.person_items.each do |x|

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

答案 3 :(得分:0)

你可以在rails中包含JSON的任何对象的深层副本,所以就这样做吧。  请记住,clone会保留冻结状态,而dup则不会。

修复错误can't modify frozen Array的最简单方法是dup这个冻结的数组;)

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