Skip to content

Commit 797381f

Browse files
committed
Fix: StrictLoadingViolationError when concatenating or setting association when non-persisted owner has primary key
Fixes rails#46689
1 parent c5fb44b commit 797381f

File tree

3 files changed

+71
-3
lines changed

3 files changed

+71
-3
lines changed

activerecord/lib/active_record/associations/association.rb

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ def initialize(owner, reflection)
4545

4646
reset
4747
reset_scope
48+
49+
@skip_strict_loading = nil
4850
end
4951

5052
# Resets the \loaded flag to +false+ and sets the \target to +nil+.
@@ -216,7 +218,7 @@ def ensure_klass_exists!
216218
end
217219

218220
def find_target
219-
if violates_strict_loading? && owner.validation_context.nil?
221+
if violates_strict_loading?
220222
Base.strict_loading_violation!(owner: owner.class, reflection: reflection)
221223
end
222224

@@ -239,7 +241,19 @@ def find_target
239241
end
240242
end
241243

244+
def skip_strict_loading(&block)
245+
skip_strict_loading_was = @skip_strict_loading
246+
@skip_strict_loading = true
247+
yield
248+
ensure
249+
@skip_strict_loading = skip_strict_loading_was
250+
end
251+
242252
def violates_strict_loading?
253+
return if @skip_strict_loading
254+
255+
return unless owner.validation_context.nil?
256+
243257
return reflection.strict_loading? if reflection.options.key?(:strict_loading)
244258

245259
owner.strict_loading? && !owner.strict_loading_n_plus_one_only?

activerecord/lib/active_record/associations/collection_association.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def build(attributes = nil, &block)
119119
def concat(*records)
120120
records = records.flatten
121121
if owner.new_record?
122-
load_target
122+
skip_strict_loading { load_target }
123123
concat_records(records)
124124
else
125125
transaction { concat_records(records) }
@@ -233,7 +233,7 @@ def empty?
233233
# and delete/add only records that have changed.
234234
def replace(other_array)
235235
other_array.each { |val| raise_on_type_mismatch!(val) }
236-
original_target = load_target.dup
236+
original_target = skip_strict_loading { load_target }.dup
237237

238238
if owner.new_record?
239239
replace_records(other_array, original_target)

activerecord/test/cases/strict_loading_test.rb

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,60 @@ def test_strict_loading_with_reflection_is_ignored_in_validation_context
162162
end
163163
end
164164

165+
def test_strict_loading_on_concat_is_ignored
166+
developer = Developer.first
167+
developer.strict_loading!
168+
169+
assert_nothing_raised do
170+
developer.audit_logs << AuditLog.new(message: "message")
171+
end
172+
end
173+
174+
def test_strict_loading_on_build_is_ignored
175+
developer = Developer.first
176+
developer.strict_loading!
177+
178+
assert_nothing_raised do
179+
developer.audit_logs.build(message: message)
180+
end
181+
end
182+
183+
def test_strict_loading_on_writer_is_ignored
184+
developer = Developer.first
185+
developer.strict_loading!
186+
187+
assert_nothing_raised do
188+
developer.audit_logs = [AuditLog.new(message: "message")]
189+
end
190+
end
191+
192+
def test_strict_loading_with_new_record_on_concat_is_ignored
193+
developer = Developer.new(id: Developer.first.id)
194+
developer.strict_loading!
195+
196+
assert_nothing_raised do
197+
developer.audit_logs << AuditLog.new(message: "message")
198+
end
199+
end
200+
201+
def test_strict_loading_with_new_record_on_build_is_ignored
202+
developer = Developer.new(id: Developer.first.id)
203+
developer.strict_loading!
204+
205+
assert_nothing_raised do
206+
developer.audit_logs.build(message: "message")
207+
end
208+
end
209+
210+
def test_strict_loading_with_new_record_on_writer_is_ignored
211+
developer = Developer.new(id: Developer.first.id)
212+
developer.strict_loading!
213+
214+
assert_nothing_raised do
215+
developer.audit_logs = [AuditLog.new(message: "message")]
216+
end
217+
end
218+
165219
def test_strict_loading_has_one_reload
166220
with_strict_loading_by_default(Developer) do
167221
ship = Ship.create!(developer: Developer.first, name: "The Great Ship")

0 commit comments

Comments
 (0)