From bda9bc013b631af2e7dba3e20474f78a561610d6 Mon Sep 17 00:00:00 2001 From: eileencodes Date: Wed, 9 Dec 2020 13:42:24 -0500 Subject: [PATCH] Fix strict loading on validations We don't want strict loading to throw an error on validations since those need to load the record in order to validate it. This change check the owners's `validation_context`. If it's `nil` then we know we're not currently validating an object in create/update. If it's set then we're validating and want to skip raising for strict loading. Fixes: #40767 --- .../active_record/associations/association.rb | 4 ++-- .../test/cases/strict_loading_test.rb | 21 +++++++++++++++++++ activerecord/test/models/developer.rb | 6 ++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb index 95e5976dea280..2d211e908c748 100644 --- a/activerecord/lib/active_record/associations/association.rb +++ b/activerecord/lib/active_record/associations/association.rb @@ -211,11 +211,11 @@ def create!(attributes = nil, &block) private def find_target - if owner.strict_loading? + if owner.strict_loading? && owner.validation_context.nil? Base.strict_loading_violation!(owner: owner.class, association: klass) end - if reflection.strict_loading? + if reflection.strict_loading? && owner.validation_context.nil? Base.strict_loading_violation!(owner: owner.class, association: reflection.name) end diff --git a/activerecord/test/cases/strict_loading_test.rb b/activerecord/test/cases/strict_loading_test.rb index c730fbe588ce2..fce25c817f92b 100644 --- a/activerecord/test/cases/strict_loading_test.rb +++ b/activerecord/test/cases/strict_loading_test.rb @@ -86,6 +86,27 @@ def test_raises_if_strict_loading_by_default_and_lazy_loading end end + def test_strict_loading_is_ignored_in_validation_context + with_strict_loading_by_default(Developer) do + developer = Developer.first + assert_predicate developer, :strict_loading? + + assert_nothing_raised do + AuditLogRequired.create! developer_id: developer.id, message: "i am a message" + end + end + end + + def test_strict_loading_with_reflection_is_ignored_in_validation_context + with_strict_loading_by_default(Developer) do + developer = Developer.first + assert_predicate developer, :strict_loading? + + developer.required_audit_logs.build(message: "I am message") + developer.save! + end + end + def test_preload_audit_logs_are_strict_loading_because_parent_is_strict_loading developer = Developer.first diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb index f4c051e691f3c..1b48362f4162f 100644 --- a/activerecord/test/models/developer.rb +++ b/activerecord/test/models/developer.rb @@ -72,6 +72,7 @@ def find_least_recent class_name: "SpecialProject" has_many :audit_logs + has_many :required_audit_logs, class_name: "AuditLogRequired" has_many :strict_loading_audit_logs, -> { strict_loading }, class_name: "AuditLog" has_many :strict_loading_opt_audit_logs, strict_loading: true, class_name: "AuditLog" has_many :contracts @@ -125,6 +126,11 @@ class AuditLog < ActiveRecord::Base belongs_to :unvalidated_developer, class_name: "Developer" end +class AuditLogRequired < ActiveRecord::Base + self.table_name = "audit_logs" + belongs_to :developer, required: true +end + class DeveloperWithBeforeDestroyRaise < ActiveRecord::Base self.table_name = "developers" has_and_belongs_to_many :projects, join_table: "developers_projects", foreign_key: "developer_id"