2010-12-19 12 views
10

Ich habe eine Person Modell & ein Item-Modell. Eine Person hat viele Gegenstände und ein Gegenstand gehört einer Person.Warum bekomme ich den Fehler 'kann eingefrorenes Hash nicht ändern'?

In diesem Code muss ich die vorhandenen Elemente für eine Person löschen, und neue von einem Parameter (der ein Array von Hashes ist) erstellen. Dann muss ich eines der Felder des Elements basierend auf einem seiner anderen Felder aktualisieren.

@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 

Auf den x.item_amount = "5" & x.item_amount = "10" Linien bekomme ich diesen Fehler:

RuntimeError in PersonsController#submit_items 
can't modify frozen hash 

Wie kann ich dieses Problem beheben? Danke fürs Lesen.

Antwort

7

würde ich

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

gibt einen gefrorenen Hash vermuten, die Sie dann Objekte erstellen verwenden, um

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

Und da seine eingefroren Sie es nicht ändern können.

Sie könnten

A eine tiefe Kopie des Objekts JSON Make

oder

B neu laden die Modellinstanz, die das Objekt Reinstanziierung sollten die Felder nicht gefrorenen machen.

Option A ist die "bessere" Lösung, aber schwierig, weil das einzige, was ich von tiefem Kopieren weiß, Serialisierung und Deserialisierung und Objekt an Ort und Stelle ist und den Rückgabewert zuweist.

+0

Vielen Dank für Ihre Antwort. Ich bin mir nicht sicher, ob ich das verstehe, ich versuche nicht, das Hash/JSON-Objekt zu ändern, ich versuche das ActiveRecord-Objekt zu ändern, das ich gerade erstellt habe. Das mag in meinem Code etwas verwirrend gewesen sein. Ich habe einige der Variablennamen geändert, um sie klarer zu machen. – ben

+0

Ich glaube ActiveSupport :: JSON.decode (params ["person_items"]) erstellt den eingefrorenen Hash.aber wenn Sie es neu laden wird ActiveRecord nur einen neuen Hash instanziieren, der nicht eingefroren ist – EnabrenTane

+0

reload funktioniert, wenn versucht wird, Eltern nach dem Löschen Kind löschen – Anwar

2

Sie können umgehen, wenn Sie die person_items erneut aus der Datenbank lesen, anstatt die Zuordnung zu verwenden. Die Assoziation ist veraltet und zeigt auf die zerstörten Zeilen.

Statt @person.person_items.each do |x|

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

6

Wenn Sie q.destroy verwenden, bevor Element Speichern dann werden Sie den Fehler erhalten. besser zuerst das Element speichern und dann zerstören.

+0

Dies ist die richtige Erklärung. – CppNoob

0

Sie können eine tiefe Kopie von jedem Objekt in Schienen JSON enthalten, also tun Sie es einfach. Denken Sie daran, dass clone den eingefrorenen Zustand beibehält, während dup dies nicht tut.

Der einfachste Weg, Fehler zu beheben can't modify frozen Array ist dup dieses gefrorene Array;)

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