#ruby-on-rails #ruby #activerecord
#ruby-on-rails #ruby #activerecord
Вопрос:
У меня такие отношения:
Parent
has_many :children
Child
belongs_to :parent
Что я хочу сделать, так это удалить родительский элемент, если дочерних элементов больше не осталось. Итак, для этого у меня есть:
Child
before_destroy :destroy_orphaned_parent
def destroy_orphaned_parent
parent.children.each do |c|
return if c != self
end
parent.destroy
end
Это работает нормально, однако я также хочу каскадировать удаление родительского элемента дочернему. Например. Обычно я бы сделал:
Parent
has_many :children, :dependent => :destroy
Это приводит к сбою сервера WEBrick при его тестировании. Я предполагаю, что это связано с бесконечным циклом последнего дочернего элемента, удаляющего родительский элемент, удаляющего дочерний элемент и т.д.
Я начинаю думать, что есть лучший способ сделать это? У кого-нибудь есть идеи? Есть ли способ предотвратить эту рекурсию?
Комментарии:
1. Подумал, что я бы добавил, что это
parent.destroy if parent.children.empty?
более лаконично, чем тот цикл с ранним завершением вdestroy_orphaned_parent
. Кроме того, если ни родителю, ни дочерним элементам не нужно выполнять какую-либо очистку, вы можете использоватьdelete
anddelete_all
вместоdestroy
иdestroy_all
, чтобы пропустить обратные вызовы.
Ответ №1:
Я добился этого следующим образом:
before_destroy :find_parent
after_destroy :destroy_orphaned_parent
def find_parent
@parent = self.parent
end
def destroy_orphaned_parent
if @parent.children.length == 0
@parent.destroy
end
end
Согласно предложению Анвара, это также может быть выполнено с помощью around
обратного вызова следующим образом:
around_destroy :destroy_orphaned_parent
def destroy_orphaned_parent
parent = self.parent
yield # executes a DELETE database statement
if parent.children.length == 0
parent.destroy
end
end
Я не тестировал вышеупомянутое решение, поэтому не стесняйтесь обновлять его, если это необходимо.
Комментарии:
1. Вместо использования обратного вызова до и после также можно использовать обратный вызов around
Ответ №2:
Некоторые идеи:
-
Вы могли бы удалить осиротевших родителей в
after_destroy
(найдите их, используя инструкцию, подобную той, что на http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/a3f12d578f5a2619 ) -
Вы могли бы установить некоторую переменную экземпляра в,
before_destroy
содержащую идентификатор родителя, затем выполнить поиск на основе этого идентификатора вafter_destroy
обратном вызове и решить, удалять ли родительский элемент там, основываясь на подсчете дочерних элементов
Комментарии:
1. еще не тестировал это, но я думаю, что ключ в том, чтобы переместить логику в after_destroy, а не в before_destroy, как у меня было в моем первоначальном сообщении
Ответ №3:
Используйте after_destroy
обратный вызов.
after_destroy :release_parent
def release_parent
if parent.children.count.zero?
parent.destroy
end
end
Использование Rails 3.2.15
Ответ №4:
Вы можете выполнить это с помощью around_destroy
обратного вызова
around_destroy :destroy_orphaned_parent
def destroy_orphaned_parent
@parent = self.parent
yield
@parent.destroy if @parent.children.length == 0
end
Ответ №5:
after_destroy :destroy_parent, if: parent_is_orphan?
private
def parent_is_orphan?
parent.children.count.zero?
end
def destroy_parent
parent.destroy
end