mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
update_column
take ruby-land input, not database-land input
In the case of serialized columns, we would expect the unserialized value as input, not the serialized value. The original issue which made this distinction, #14163, introduced a bug. If you passed serialized input to the method, it would double serialize when it was sent to the database. You would see the wrong input upon reloading, or get an error if you had a specific type on the serialized column. To put it another way, `update_column` is a special case of `update_all`, which would take `['a']` and not `['a'].to_yaml`, but you would not pass data from `params` to it. Fixes #18037
This commit is contained in:
parent
e4c9bd9828
commit
dd8b5fb9d3
4 changed files with 27 additions and 3 deletions
|
@ -9,6 +9,10 @@ module ActiveRecord
|
|||
FromUser.new(name, value, type)
|
||||
end
|
||||
|
||||
def with_cast_value(name, value, type)
|
||||
WithCastValue.new(name, value, type)
|
||||
end
|
||||
|
||||
def null(name)
|
||||
Null.new(name)
|
||||
end
|
||||
|
@ -58,6 +62,10 @@ module ActiveRecord
|
|||
self.class.from_database(name, value, type)
|
||||
end
|
||||
|
||||
def with_cast_value(value)
|
||||
self.class.with_cast_value(name, value, type)
|
||||
end
|
||||
|
||||
def type_cast(*)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
@ -93,6 +101,16 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
class WithCastValue < Attribute # :nodoc:
|
||||
def type_cast(value)
|
||||
value
|
||||
end
|
||||
|
||||
def changed_in_place_from?(old_value)
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
class Null < Attribute # :nodoc:
|
||||
def initialize(name)
|
||||
super(name, nil, Type::Value.new)
|
||||
|
|
|
@ -73,7 +73,7 @@ module ActiveRecord
|
|||
if should_type_cast
|
||||
@attributes.write_from_user(attr_name, value)
|
||||
else
|
||||
@attributes.write_from_database(attr_name, value)
|
||||
@attributes.write_cast_value(attr_name, value)
|
||||
end
|
||||
|
||||
value
|
||||
|
|
|
@ -39,6 +39,10 @@ module ActiveRecord
|
|||
attributes[name] = self[name].with_value_from_user(value)
|
||||
end
|
||||
|
||||
def write_cast_value(name, value)
|
||||
attributes[name] = self[name].with_cast_value(value)
|
||||
end
|
||||
|
||||
def freeze
|
||||
@attributes.freeze
|
||||
super
|
||||
|
|
|
@ -243,8 +243,9 @@ class SerializedAttributeTest < ActiveRecord::TestCase
|
|||
t = Topic.create(content: "first")
|
||||
assert_equal("first", t.content)
|
||||
|
||||
t.update_column(:content, Topic.type_for_attribute('content').type_cast_for_database("second"))
|
||||
assert_equal("second", t.content)
|
||||
t.update_column(:content, ["second"])
|
||||
assert_equal(["second"], t.content)
|
||||
assert_equal(["second"], t.reload.content)
|
||||
end
|
||||
|
||||
def test_serialized_column_should_unserialize_after_update_attribute
|
||||
|
@ -253,5 +254,6 @@ class SerializedAttributeTest < ActiveRecord::TestCase
|
|||
|
||||
t.update_attribute(:content, "second")
|
||||
assert_equal("second", t.content)
|
||||
assert_equal("second", t.reload.content)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue