This project is archived and is in readonly mode.
Dirty tracking on serialized columns is broken
Reported by Andre Foeken | June 7th, 2008 @ 01:48 AM | in 3.x
class Monkey < ActiveRecord::Base
serialize legs, Array
end
Now in the console:
>> m = Monkey.create
>> m.changed? => false
>> m.legs = [1,2,3]
>> m.changed? => false
Comments and changes to this ticket
-
Jeremy Kemper June 11th, 2008 @ 07:59 PM
- Milestone set to 2.1.1
- State changed from new to open
- Assigned user set to Jeremy Kemper
-
Ernie Miller July 3rd, 2008 @ 02:51 PM
- Tag set to activerecord, bug, dirty
Is this bug valid? I can't reproduce in Rails 2.1, anyway.
class Monkey < ActiveRecord::Base
serialize :legs, Array
end
And in script/console:
Loading development environment (Rails 2.1.0)
>> m = Monkey.create
=> #
>> m.changed?
=> false
>> m.legs = [1,2,3,4]
=> [1, 2, 3, 4]
>> m.changed?
=> true
-
Ernie Miller July 3rd, 2008 @ 02:58 PM
Hm. Trying once more since Lighthouse formatting eluded me.
class Monkey < ActiveRecord::Base
serialize :legs, Array
end
and
Loading development environment (Rails 2.1.0)
>> m = Monkey.create
=> #
>> m.changed?
=> false
>> m.legs = [1,2,3,4]
=> [1, 2, 3, 4]
>> m.changed?
=> true
-
Ernie Miller July 3rd, 2008 @ 02:59 PM
Eh, I give up. Trying Textile but it's not working. You get the idea.
-
Ripta Pasay July 12th, 2008 @ 09:19 PM
I couldn't reproduce this either. I did, however, find that dirty tracking doesn't work with in-place manipulation of said serialized column:
>> m.legs => [1, 2, 3] >> m.changed? => false >> m.legs << 4 => [1, 2, 3, 4] >> m.changed? => false
IMHO, AR should let the object itself figure out whether it's been changed or not. If anything, AR could provide a way for either: (a) the serialized object to notify AR that the attribute has been changed, or (b) AR to optionally query the serialized object when its own #changed? is called.
-
quake wang July 13th, 2008 @ 04:04 AM
I can't reproduce this error in rails 2.1, however, dirty checking is broken is this case:
>> m.reload
>> m.legs = m.legs << 4
>> m.changed?
=> false
-
Jeremy Kemper July 16th, 2008 @ 12:20 AM
- State changed from open to invalid
-
barunio March 28th, 2009 @ 12:01 AM
Why was this ticket changed to 'invalid'? This is still broken as far as I can tell (yes, AR knows if the field went from nil to an array, but it still can't tell if an existing serialized array was modified).
If you have a serialized field named "array_field", then "array_field_was" method doesn't return what array_field was, and "array_field_changed?" doesn't tell you if array_field was changed. To me that looks like clearly broken behavior (and I don't think the docs anywhere specify that one ought to expect different behavior for serialized fields)..
-
Lluís May 14th, 2010 @ 01:32 PM
This still happens on Rails 2.3.5
if serialized changes of type (nil to Array, Array to Hash..) changed? method returns true, but not if serialized changes conserving its type (adding an element to array..) -
Neeraj Singh May 16th, 2010 @ 04:05 AM
This is no longe an issue with rail3.
This is what I found on rails edge.
ActiveRecord::Schema.define(:version => 20100516025941) do create_table "users", :force => true do |t| t.string "name" t.text "bio" end end class User < ActiveRecord::Base serialize :bio end #rails -v : Rails 3.0.0.beta3 #ruby -v : ruby 1.8.7 (2010-01-10 patchlevel 249) [i686-darwin10.3.0] > u = User.create > u.changed? => false > u.bio = 'w' => "w" > u.changed? => true > u.changes => {"bio"=>[nil, "w"]}
-
Karl Varga July 21st, 2010 @ 01:26 AM
- Importance changed from to High
I've just run into this now on Rails 2.3.8.
Neeraj, that's not a good test. You want to use a Hash and add a new key, then you'll see what happens.
In this output I have a User model with a prefs serialized Hash:
(rdb:158) current_user.changed? false
(rdb:158) current_user.prefs[:some_new_key] = 42 42
(rdb:158) current_user.changed? false -
Neeraj Singh July 21st, 2010 @ 01:59 AM
In rails 3 this is what I am getting
class User < ActiveRecord::Base serialize :bio def self.lab u = User.create assert !u.changed? u.bio = {:social => :github} assert u.changed? u.reload u.bio = {} u.bio[:social] = :github assert u.changed? end end
All assertions are passing.
-
Karl Varga July 22nd, 2010 @ 09:16 PM
@neeraj, if u.bio is nil then assigning a hash to it will show a change because it is no longer nil.
Try it with a user that has bio already set and saved, then assign a key:
u.bio = {}
u.save
u.bio[:social] = :github
assert u.changed? -
Neeraj Singh July 22nd, 2010 @ 11:30 PM
- Milestone changed from 2.1.1 to 3.x
- State changed from invalid to open
- Tag changed from activerecord, bug, dirty to rails 3, activerecord, bug, dirty
- Assigned user changed from Jeremy Kemper to Neeraj Singh
- Importance changed from High to Low
@Karl thanks
class User < ActiveRecord::Base serialize :bio def self.lab u = User.create(:bio => {:eye_color => "black"}) assert !u.changed? u.bio = {:social => :github} assert u.changed? # passes u.reload u.bio[:eye_color] = :green assert u.changed? # Fails end end
Opening the ticket.
PS: I think the case where it is failing is okay. That is because changed? works by trakcing assigning changed valeu and in this case in-memory value is being changed. Nothing is being assigned.
-
José Valim July 22nd, 2010 @ 11:38 PM
- State changed from open to invalid
Yes, this ticket is invalid because we cannot possibly track changes in place. This is mentioned in the documentation. You can either: 1) do not change serialized attributes in place or 2) call attribute_will_change!
-
Neeraj Singh July 22nd, 2010 @ 11:46 PM
In case someone wants to see the complete solution. Here it is with the usage of attribute_will_change!
class User < ActiveRecord::Base serialize :bio def self.lab u = User.create(:bio => {:eye_color => "black"}) assert !u.changed? u.bio = {:social => :github} assert u.changed? u.reload u.send(:attribute_will_change!, :bio) u.bio[:eye_color] = :green assert u.changed? end end
-
Glenn Vanderburg August 11th, 2010 @ 09:55 PM
José, you said this is mentioned in the documentation. Where?
And here's a more idiomatic example of how to do the right thing with attribute_will_change!:
class User < ActiveRecord::Base serialize :bio def self.lab u = User.create(:bio => {:eye_color => "black"}) assert !u.changed? u.bio = {:social => :github} assert u.changed? u.reload u.bio_will_change! u.bio[:eye_color] = :green assert u.changed? end end
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile »
<h2 style="font-size: 14px">Tickets have moved to Github</h2>
The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>
People watching this ticket
Tags
Referenced by
- 391 Dirty tracking doesn't catch in-place changes to field values I suspect this is a duplicate of Andre Foeken's bug #360.
- 608 Dirty checking is broken for string join This is the same problem as http://rails.lighthouseapp.c...
- 3464 serialize_fields not recording changes This has come up a couple times: see tickets #2764 and #360.
- 3474 _was doesn't work correctly for serialized fields with nested elements Note that this isn't about partial updates (like #2764) o...