In the OpenProject application we have two models:
CustomField
class CustomField < ActiveRecord::Base
has_many :custom_options, -> { order(position: :asc) }, dependent: :delete_all
accepts_nested_attributes_for :custom_options
...
end
CustomOption
class CustomOption < ActiveRecord::Base
belongs_to :custom_field, touch: true
...
end
Then in the custom field controller we modify the options of a custom field via mass assignment and save the record:
@custom_field.attributes = get_custom_field_params
if @custom_field.save
...
Because of touch: true
being configured on the custom_field
association in CustomOption
I would have expected the custom field's updated_at
attribute to be updated at this point which does not happen however. The log does not show any SQL requests similar to UPDATE custom_fields SET update_at = ....
. The changes to the custom options themselves, regardless of whether it is adding a custom option or modifying one, are correctly persisted.
Debugging showed:
custom_field.custom_options.build
and a subsequent custom_field.save
custom_field.custom_options.create
CustomOption.create custom_field: x
custom_field.custom_option[x].destroy
I know I could work around the problem by defining after_save
callbacks, but this behaviour really irks me as it was unexpected.
Is the behaviour as outlined above desired and documented? If so, where can I find this information?
The problem is that if the parent model has any after_commit
callback (maybe some others), :touch
stops working when autosaving associated records, even if the callback is not doing anything.
Your CustomField
model has after_commit
callback coming from acts_as_list
gem. This breaks :touch
.
There was a similar issue https://github.com/rails/rails/issues/26726 . That issue was associated with accepts_nested_attributes_for
, but I am sure that accepts_nested_attributes_for
has nothing to do with it, at least in your case.
Added by the author of the question (@ulferts):
Defining inverse_of: 'custom_field'
on the custom_options
association in CustomField
seems to circumvent the problem for unknown reasons.
has_many :custom_options,
-> { order(position: :asc) },
dependent: :delete_all,
inverse_of: 'custom_field'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With