Don't validate non dirty association targets

Fixes #36581.

This fixes an issue where validations would return differently when a previously saved invalid association was loaded between calls:

    assert_equal true, squeak.valid?
    assert_equal true, squeak.mouse.present?
    assert_equal true, squeak.valid?

Here the second assert would return

    Expected: true
    Actual: false

Limiting validations to associations that would be normally saved (using autosave: true) due to changes means that loading invalid associated relations will not change the return value of the parent relations's `valid?` method.
This commit is contained in:
Will Jessop 2019-07-13 20:52:32 +01:00
parent 88b91299f3
commit 6ea80b6103
No known key found for this signature in database
GPG Key ID: ABD049BCC56E8A52
5 changed files with 37 additions and 1 deletions

View File

@ -302,7 +302,7 @@ module ActiveRecord
def validate_single_association(reflection)
association = association_instance_get(reflection.name)
record = association && association.reader
association_valid?(reflection, record) if record
association_valid?(reflection, record) if record && record.changed_for_autosave?
end
# Validate the associated records if <tt>:validate</tt> or

View File

@ -13,12 +13,14 @@ require "models/developer"
require "models/computer"
require "models/invoice"
require "models/line_item"
require "models/mouse"
require "models/order"
require "models/parrot"
require "models/pirate"
require "models/project"
require "models/ship"
require "models/ship_part"
require "models/squeak"
require "models/tag"
require "models/tagging"
require "models/treasure"
@ -386,6 +388,20 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
assert_predicate auditlog, :valid?
end
def test_validation_does_not_validate_non_dirty_association_target
mouse = Mouse.create!(name: "Will")
Squeak.create!(mouse: mouse)
mouse.name = nil
mouse.save! validate: false
squeak = Squeak.last
assert_equal true, squeak.valid?
assert_equal true, squeak.mouse.present?
assert_equal true, squeak.valid?
end
end
class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttributes < ActiveRecord::TestCase

View File

@ -0,0 +1,6 @@
# frozen_string_literal: true
class Mouse < ActiveRecord::Base
has_many :squeaks, autosave: true
validates :name, presence: true
end

View File

@ -0,0 +1,6 @@
# frozen_string_literal: true
class Squeak < ActiveRecord::Base
belongs_to :mouse
accepts_nested_attributes_for :mouse
end

View File

@ -563,6 +563,10 @@ ActiveRecord::Schema.define do
t.string :type
end
create_table :mice, force: true do |t|
t.string :name
end
create_table :movies, force: true, id: false do |t|
t.primary_key :movieid
t.string :name
@ -843,6 +847,10 @@ ActiveRecord::Schema.define do
end
end
create_table :squeaks, force: true do |t|
t.integer :mouse_id
end
create_table :prisoners, force: true do |t|
t.belongs_to :ship
end