Как предотвратить запуск обратного вызова модели, если инициируется обратный вызов ассоциации? - PullRequest
0 голосов
/ 22 марта 2020

У меня есть модель PositionGroup, которая has_many :positions.

Когда бы ни была добавлена ​​или сохранена позиция, я хочу обновить атрибут на PositionGroup. Например, когда новая позиция была добавлена ​​к PositionGroup, я хотел бы добавить position.volume к существующей position_group.total_units.

Итак, рассмотрим следующий пример:

pg.total_units = 100 
position2.volume = 50 
# then pg.total_units should be updated to:
pg.total_units = 150

Я понимаю, что могу сделать это с помощью обратного вызова ассоциации, и это нормально.

Проблема в том, что происходит, когда я хочу обновить объем предыдущей позиции. Если я добавлю before_save или after_save, то этот обратный вызов также будет запущен после срабатывания обратного вызова after_add - таким образом, искусственно раздувая цифру pg.total_units.

Как мне решить эту проблему

Ответы [ 2 ]

0 голосов
/ 22 марта 2020

Если я правильно понял вопрос, вот один наивный способ предотвратить эту искусственную инфляцию:

class Position < ApplicationRecord
  belongs_to :position_group
  before_save :update_position_group_total_units

  def update_position_group_total_units
    delta = volume - (volume_was || 0)
    position_group.update!(total_units:  position_group.total_units + delta)
  end
end
class PositionGroup < ApplicationRecord
  has_many :positions
end

См. Документацию здесь: https://api.rubyonrails.org/classes/ActiveModel/Dirty.html

Предостережение: если по какой-то причине сохранение не удастся, position_group все равно обновится, что приведет к другому виду просчетов.

Мне больше всего нравится сенсорный подход , как вы обнаружили:

class Position < ApplicationRecord
  belongs_to :position_group, touch: true
end
class PositionGroup < ApplicationRecord
  has_many :positions

  after_touch :recompute_total_units

  private

  def recompute_total_units
    update_column(:total_units, positions.sum(:volume))
  end
end
0 голосов
/ 22 марта 2020

Я нашел решение.

По сути, вместо использования многих after_save, before_save & after_add обратных вызовов, я просто использую touch: true на одной модели и after_touch на другой. Это решило все мои проблемы.

Для большей ясности, на моей Position модели я добавил:

belongs_to :position_group, touch: true

Затем на моей PositionGroup модели я добавил:

after_touch :recompute_total_units

  private

  def recompute_total_units
    update_column(:total_units, positions.sum(:volume))
  end

Вуаля.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...