Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

belongs_to touch: true not fired on association.build and nested attribute assignment

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:

  • The same erroneous behaviour when using custom_field.custom_options.build and a subsequent custom_field.save
  • The desired behaviour when using custom_field.custom_options.create
  • The desired behaviour when using CustomOption.create custom_field: x
  • The desired behaviour when using 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?

like image 777
ulferts Avatar asked Jan 30 '23 12:01

ulferts


1 Answers

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'
like image 145
chumakoff Avatar answered Apr 07 '23 02:04

chumakoff