Skip to content

Commit 210bc5b

Browse files
committed
Assign id attributes first in Active Record attribute assignment
Instead of assigning hashes later, we can assign IDs first to maintain the same behaviour for nested attribute assignment. Deferring hash assignment causes problems in store scenarios where an attribute can be assigned two different ways, which had worked since Rails 7.2.
1 parent 281a00a commit 210bc5b

File tree

2 files changed

+20
-9
lines changed

2 files changed

+20
-9
lines changed

activerecord/lib/active_record/attribute_assignment.rb

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,30 @@ module ActiveRecord
44
module AttributeAssignment
55
private
66
def _assign_attributes(attributes)
7-
multi_parameter_attributes = nested_parameter_attributes = nil
7+
foreign_keys = foreign_key_column_names
8+
foreign_key_attributes = nil
9+
normal_attributes = {}
10+
multi_parameter_attributes = nil
811

912
attributes.each do |k, v|
1013
key = k.to_s
1114

12-
if key.include?("(")
15+
if key.in?(foreign_keys)
16+
(foreign_key_attributes ||= {})[key] = v
17+
elsif key.include?("(")
1318
(multi_parameter_attributes ||= {})[key] = v
14-
elsif v.is_a?(Hash)
15-
(nested_parameter_attributes ||= {})[key] = v
1619
else
17-
_assign_attribute(key, v)
20+
normal_attributes[key] = v
1821
end
1922
end
2023

21-
assign_nested_parameter_attributes(nested_parameter_attributes) if nested_parameter_attributes
24+
foreign_key_attributes.each { |key, value| _assign_attribute(key, value) } if foreign_key_attributes
25+
normal_attributes.each { |key, value| _assign_attribute(key, value) }
2226
assign_multiparameter_attributes(multi_parameter_attributes) if multi_parameter_attributes
2327
end
2428

25-
# Assign any deferred nested attributes after the base attributes have been set.
26-
def assign_nested_parameter_attributes(pairs)
27-
pairs.each { |k, v| _assign_attribute(k, v) }
29+
def foreign_key_column_names
30+
self.class._reflections.values.grep(Reflection::BelongsToReflection).map(&:foreign_key)
2831
end
2932

3033
# Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done

activerecord/test/cases/store_test.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,14 @@ class StoreTest < ActiveRecord::TestCase
220220
assert_equal "blue", user.color[:jenny]
221221
end
222222

223+
test "update store and accessor" do
224+
user = Admin::User.find_by_name("Jamis")
225+
user.update(settings: { homepage: "not rails" }, homepage: "rails")
226+
227+
assert_equal "rails", user.settings[:homepage]
228+
assert_equal "rails", user.homepage
229+
end
230+
223231
def test_convert_store_attributes_from_Hash_to_HashWithIndifferentAccess_saving_the_data_and_access_attributes_indifferently
224232
user = Admin::User.find_by_name("Jamis")
225233
assert_equal "symbol", user.settings[:symbol]

0 commit comments

Comments
 (0)