Rails — проверяет, был ли родительский объект уничтожен и вызывает destroy для зависимых

#ruby-on-rails #activemodel

#ruby-on-rails #activemodel

Вопрос:

У меня есть следующее:

 class ModelA < ApplicationRecord
  has_many :model_bs, dependent: :destroy
end

class ModelB < ApplicationRecord
  belongs_to :model_a
  after_destroy :action_only_if_model_a_exists

  private

  def action_only_if_model_a_exists
    # Do things
  end
end
  

Когда я вызываю model_a.destroy , мне нужно иметь возможность определить в action_only_if_model_a_exists обратном вызове в ModelB, существует ли еще связанная модель или она также вот-вот будет уничтожена.

Есть ли хороший встроенный в Rails способ сделать это или мне нужно пойти по пути установки флага в ModelA в более раннем обратном вызове (например, before_destroy ), который я могу затем проверить в обратном вызове ModelB?

Редактировать

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

 > model_a.persisted?
true

> model_a.destroyed?
false

> model_a.frozen?
false
  

Комментарии:

1. Вы ожидаете обнулить идентификаторы модели b в качестве стратегии или фактически удалить записи? Кроме того, вас беспокоит задержка в несколько миллисекунд или на самом деле есть какой-то случай, когда вы на самом деле не ожидаете, что modela будет уничтожена, даже если был вызван modela.destroy?

2. Я ожидаю удалить записи.

3. И содержимое action_only_if_model_a_exists метода запускает асинхронный процесс, для завершения которого требуется несколько минут, и произойдет сбой, если соответствующая модель больше не существует.

4. Я в замешательстве. Если вы вызываете modela.destroy, то обязательно к моменту уничтожения modelb modela должна быть уничтожена, а также сразу после завершения процесса db, поскольку вы вызвали метод для этого, чтобы сделать именно это. Кажется, лучшим способом было бы вызвать modela.modelb.destroy_all с вашего контроллера, затем, когда вы подтвердите, что процесс modelb завершен, явно уничтожьте modela во втором вызове.

5. Порядок действий, по-видимому, следующий: model_a before_destroy callback model_b before_destroy callback model_b after_destroy callback model_a after_destroy_callback Это означает, что когда model_b уничтожается, model_a все еще существует в базе данных.

Ответ №1:

Вы можете использовать destroyed_by_association атрибут в дочернем элементе, чтобы увидеть, уничтожается ли объект как часть dependent: :destroy от его родительского элемента.

Ответ №2:

Я не смог найти хороший способ сделать это, поэтому я остановился на следующем:

 class ModelA < ApplicationRecord
  attr_accessor :destroying?
  has_many :model_bs, dependent: :destroy

  def destroy
    self.destroying? = true
    super
  end
end

class ModelB < ApplicationRecord
  belongs_to :model_a
  after_destroy :action_only_if_model_a_exists

  private

  def action_only_if_model_a_exists
    if !model_a.destroying?
      # Do things
    end
  end
end
  

Ответ №3:

Вероятно, самый простой способ достичь этого — использовать before_destroy обратный вызов в ModelA для вызова требуемой функции для всех связанных с ней ModelB функций

 class ModelA < ApplicationRecord
  has_many :model_bs, dependent: :destroy

  before_destroy { |model_a| model_a.model_bs.each { |model_b| model_b. action_only_if_model_a_exists } }
end
  

Конечно, action_only_if_model_a_exists не должен быть приватным.

Кроме того, вы захотите учесть, сколько model_bs у любого данного model_a объекта, вероятно, будет, и при необходимости рассмотрите возможность использования in_batches вместо each .

Обновить

Хорошо, основываясь на вашем обновленном объяснении, и мое понимание теперь противоположно тому, что было ранее, как насчет следующего?

 class ModelB < ApplicationRecord
  belongs_to :model_a
  after_destroy :action_if_not_called_from_model_a

  private

  def action_if_not_called_from_model_a
    if !caller.join.match(/model_a/)
      # Do things
    end
  end
end
  

Комментарии:

1. Я думаю, что мой вопрос, должно быть, был неясен — это противоположно поведению, которого я на самом деле добиваюсь: я хочу только action_only_if_model_a_exists запускать, если model_b.destroy вызывается, а не если model_a.destroy вызывается.