-
Notifications
You must be signed in to change notification settings - Fork 21.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rails 6.1 (can't modify frozen attributes) error when destroying child record with touch: true #40943
Comments
I have the same problem without awesome_nested_set and can at least provide the following backtrace for
Couldn't reproduce it in a clean app for now either. |
And I also noticed it only happens if the association is not preloaded, so for example: class Event < ApplicationRecord
has_many :applications, class_name: 'EventApplication', inverse_of: :event, dependent: :destroy
end
class EventApplication < ApplicationRecord
belongs_to :event, inverse_of: :applications, touch: true
end
ea = EventApplication.last
ea.destroy
# => FrozenError
# but this works:
ea = EventApplication.last
ea.event # load event here!
ea.destroy
# => no error Looks like setting/loading the association while destroying the record is a problem. Still can't find out why it happens in my (complex) app but not in a clean test app. |
Woohoo, finally got a reproducible test case. The culprit lies within To reproduce it with postgresql and
To reproduce it with @javinto's original report: Work around if you do not rely on it: Set |
Thank you Markus for figgering out. I just edited my Bugtest to be able to reproduce it. It seems to be an inverse_of problem |
Given you already can reproduce in a script, can you try to bisect the commit that broke this case and possibly open a PR fixing? |
Sure, will give it a shot tomorrow. |
The offending commit is d45c9ad – so it was broken from the beginning when has_many inversing was added. Will see later if I can find a fix for it. |
I proposed a fix in #40969 ... was a lot of digging through Rails' code for such a simple fix :-) |
For the record here is a simpler bug template that triggers the error – only # frozen_string_literal: true
require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
# Activate the gem you are reporting the issue against.
gem "rails", github: "rails/rails", branch: '6-1-stable'
gem "sqlite3"
end
require "active_record"
require "minitest/autorun"
require "logger"
require 'active_support/testing/assertions'
ActiveRecord::Base.has_many_inversing = true
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
create_table :events, force: true do |t|
end
create_table :event_applications, force: true do |t|
t.references :event, null: false
end
end
class Event < ActiveRecord::Base
has_many :event_applications
end
class EventApplication < ActiveRecord::Base
belongs_to :event, inverse_of: :event_applications, touch: true
end
class BugTest < Minitest::Test
include ActiveSupport::Testing::Assertions
def test_association_stuff
event = Event.create!
event.event_applications.create!
assert_difference 'EventApplication.count', -1 do
EventApplication.last.destroy
end
end
end |
…ith inverse `has_many_inversing` adds records to a has_many association. It does so with destroyed records, too. So if a child was destroyed with a `touch: true` association on the parent *and* the parent was not loaded, it tried to load the parent to touch it. While loading the parent it added the child record to the parent's has_many association. The logic doing this always set the child's parent id – even if it was correct/the same already. But since the child is destroyed, it resulted in a `FrozenError`. This commit prevents doing the unnecessary setting of the identical id and therefore fixes this error. Fixes rails#40943 by not doing an unneeded attribute set.
…ith inverse `has_many_inversing` adds records to a has_many association. It does so with destroyed records, too. So if a child was destroyed with a `touch: true` association on the parent *and* the parent was not loaded, it tried to load the parent to touch it. While loading the parent it added the child record to the parent's has_many association. The logic doing this always set the child's parent id – even if it was correct/the same already. But since the child is destroyed, it resulted in a `FrozenError`. This commit prevents doing the unnecessary setting of the identical id and therefore fixes this error. Fixes #40943 by not doing an unneeded attribute set. Closes #40969. [Markus Doits + Rafael Mendonça França]
Steps to reproduce
I found this one using AweSomeNestedSet gem, but can reproduce it in a clean model situation. I have created a MiniTest but unfortunately the error does not trigger there. See the Console steps further on where I can reproduce it.
CORRECTION: as 'doits' found out, I added the config.active_record.has_many_inversing=true option which triggers te MiniTest to generate the expected errors.
It's important to reload the child. Without it, it does not occur. Both testcases should fail. The console steps to reproduce:
Removing the inverse_of option with the belongs_to associations solves the problem.
I have no idea why the MiniTest is not able to reproduce it.
Expected behavior
I expect the child record to be destroyed and the parent to be touched.
Actual behavior
The child record is not destroyed but triggers a FrozenError (can't modify frozen attributes)
System configuration
Rails 6.1.0:
Ruby 2.7.2:
The text was updated successfully, but these errors were encountered: