Почему ActiveRecord has_many использует delete_all вместо destroy_all? - PullRequest
2 голосов
/ 12 января 2010

У меня есть модель, у которой много детей. Я устанавливал / удалял детей так:

mymodel.children_ids = [1,2,3]
mymodel.save #add the children
mymodel.children_ids = [1]
mymodel.save #remove children 2,3

Это работает просто отлично, но я только что понял, что ни один из обратных вызовов (т. Е. after_destroy) не вызывается для дочерней модели.

После некоторого копания оказывается, что выполняется функция delete_all, а не destroy_all. Как правильно указано в документации, функция delete_all не запускает обратные вызовы, так что, есть ли способ изменить это поведение?

Спасибо.

Ответы [ 4 ]

1 голос
/ 23 сентября 2010

У меня была похожая проблема с обратными вызовами. Решил это, используя alias_method_chain для переопределения установщика по умолчанию.

  def product_categories_with_destroy=(product_categories)
    unless new_record? 
      (self.product_categories - product_categories).each(&:destroy)
    end

    self.product_categories_without_destroy = product_categories
  end
  alias_method_chain :product_categories=, :destroy

Подробнее о alias_method_chain:

http://yehudakatz.com/2009/03/06/alias_method_chain-in-models/

1 голос
/ 12 января 2010

Что-то вроде:

mymodel.children.find([2,3]).each {|c| c.destroy }

сделает работу. Это не совсем то, что вы хотели, но я надеюсь, что это поможет.

1 голос
/ 13 января 2010

Для тех, кто заинтересован, я добавил следующий monkeypatch, чтобы заставить has_many выполнить destroy_all, а не delete_all. Там может быть лучший способ, поэтому я открыт для предложений.

module ActiveRecord 
  module Associations
    class HasManyThroughAssociation < HasManyAssociation       
      def delete_records(records)
        klass = @reflection.through_reflection.klass
        records.each do |associate|
          klass.destroy_all(construct_join_attributes(associate)) #force auditing by using destroy_all rather than delete all
        end
      end
    end
  end
end
1 голос
/ 12 января 2010

delete_all выполняется ... только потому что? Это то, что, по мнению основной команды Rails, должно быть вызвано в этом случае. Однако вы можете явно вызвать метод destroy_all для дочерних элементов модели, но не использовать этот тип запроса. Похоже, вы устанавливаете дочерние идентификаторы напрямую, не используя методы build() или destroy().

Да, вы можете перезаписать его функциональность. Вы можете исправить это и поместить в /vendor, переписав тот же блок кода с помощью destroy_all. Затем используйте команду send, чтобы заменить базовую функциональность ActiveRecord своей собственной.

...