1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Prevent has_one from touching parent record unless persisted

Previously, if `build_association` was called multiple times for a `has_one` association but never committed to the database, the first newly-associated record would trigger `touch` during the attempted removal of the record.

For example:

    class Post < ActiveRecord::Base
      has_one :comment, inverse_of: :post, dependent: :destroy
    end

    class Comment < ActiveRecord::Base
      belongs_to :post, inverse_of: :comment, touch: true
    end

    post = Post.create!
    comment_1 = post.build_comment
    comment_2 = post.build_comment

When `comment_2` is initialized, the `has_one` would attempt to destroy `comment_1`, triggering a `touch` on `post` from an association record that hasn't been committed to the database.

This removes the attempt to delete an associated `has_one` unless it’s persisted.
This commit is contained in:
Josh 2020-04-13 20:11:46 -05:00
parent a4deb63798
commit ba3ef762fc
3 changed files with 27 additions and 1 deletions

View file

@ -1,3 +1,9 @@
* Prevent `build_association` from `touching` a parent record if the record isn't persisted for `has_one` associations.
Fixes #38219
*Josh Brody*
* Add support for `if_not_exists` option for adding index.
The `add_index` method respects `if_not_exists` option. If it is set to true

View file

@ -81,7 +81,9 @@ module ActiveRecord
target.delete
when :destroy
target.destroyed_by_association = reflection
target.destroy
if target.persisted?
target.destroy
end
else
nullify_owner_attributes(target)
remove_inverse_instance(target)

View file

@ -851,4 +851,22 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_not author.destroy
end
end
class SpecialCar < ActiveRecord::Base
self.table_name = "cars"
has_one :special_bulb, inverse_of: :car, dependent: :destroy, class_name: "SpecialBulb", foreign_key: "car_id"
end
class SpecialBulb < ActiveRecord::Base
self.table_name = "bulbs"
belongs_to :car, inverse_of: :special_bulb, touch: true, class_name: "SpecialCar"
end
def test_has_one_with_touch_option_on_nonpersisted_built_associations_doesnt_update_parent
car = SpecialCar.create(name: "honda")
assert_queries(1) do
car.build_special_bulb
car.build_special_bulb
end
end
end