Почему «before_remove» не запускается в этой ассоциации has_many? - PullRequest
9 голосов
/ 17 января 2012

Я хочу запустить некоторый код, прежде чем объект будет удален из ассоциации has_many.

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

class Person < ActiveRecord::Base
  has_many :limbs, before_remove: :print_message

  def print_message
    puts 'removing a limb'
  end
end

class Limb < ActiveRecord::Base
  belongs_to :person
end

Хотя этокод должен вывести «удаление конечности» во время уничтожения конечности, но это не так.

p = Person.create;
l = Limb.create person: p;
p.limbs.first.destroy
# SQL (2.1ms)  DELETE FROM "limbs" WHERE "limbs"."id" = ?  [["id", 5]]
# => #<Limb id: 5, person_id: 3, created_at: "2012-01-17 11:28:01", updated_at: "2012-01-17 11:28:01"> 

Почему это действие destroy не приводит к тому, что метод print_messageвыполнить?

РЕДАКТИРОВАТЬ - существует ли этот before_remove обратный вызов?

Несколько человек спросили, существует ли этот обратный вызов.Хотя я могу найти очень немного дальнейших ссылок на него, это задокументировано в документации Rails:

http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Association+callbacks

Это скорее обратный вызов ассоциации, а не корневой ответ ActiveRecord

Редактировать 2 - почему бы просто не использовать before_destroy на лимбе?

Некоторые люди спрашивают, почему я не использую обратный вызов before_destroy на лимбе.Причина в том, что я хочу, чтобы человек проверял, есть ли минимальное количество конечностей и что последняя никогда не уничтожается.Это исходная проблема: Как вы гарантируете, что has_many всегда "имеет минимум"?

Ответы [ 3 ]

18 голосов
/ 17 января 2012

before_remove обратный вызов существует как опция в Обратные вызовы ассоциаций . Это не то же самое, что before_destroy, который является обратным вызовом ActiveRecord .

Вот как вы его используете:

class Person < ActiveRecord::Base
  has_many :limbs, :before_remove => :print_message

  def print_message(limb)
    # limb variable references to a Limb instance you're removing
    # ( do some action here )
    # ...
  end
end

class Limb < ActiveRecord::Base
  belongs_to :person
end

Вы также неправильно вызываете метод remove.

p = Person.create
l = Limb.create(:person => p)
p.limbs.first.destroy 

Здесь вы вызываете это на Limb экземпляре, поэтому ничего не срабатывает. Позвоните в ассоциацию, которую вы создали:

p = Person.create
l = Limb.create(:person => p)
p.limbs.destroy(l)

EDIT

Для сохранения минимума связанных объектов вы можете сделать что-то вроде этого:

class Person < ActiveRecord::Base
  has_many :limbs, :before_remove => :preserve_mimimum

  def preserve_minimum(limb)
    raise "Minimum must be preserved" if limbs.count == 1
  end
end

class Limb < ActiveRecord::Base
  belongs_to :person
end

Это, однако, не срабатывает на p.limbs.destroy_all, поэтому вы должны сделать что-то вроде этого p.limbs.each {|l| p.limbs.destroy(l)}

Почему не срабатывает destroy_all?

Из-за этого:

def destroy_all(conditions = nil)
   find(:all, :conditions => conditions).each { |object| object.destroy }
end

Он перебирает каждый элемент ассоциации и выполняет действие уничтожения для объекта, а не для ассоциации, поэтому.

2 голосов
/ 17 января 2012

Заменить before_remove на before_destroy.

Редактировать - обработка минимального количества конечностей

class Limb < ActiveRecord::Base
  belongs_to :creature
  before_destroy :count_limbs

  def count_limbs
    return false if self.creature.limbs.length <= self.creature.min_limbs
  end
end

Это возвращение ложной воли, я верю, остановит ее от уничтожения. Хотя я могу ошибаться

0 голосов
/ 17 января 2012

Не могу сказать, что когда-либо использовал обратный вызов before_remove и не уверен, что он существует.

Обратный вызов before destroy должен быть скорее на модели Limb и выглядеть следующим образом:

class Limb < ActiveRecord::Base
  belongs_to :person
  before_destroy :print_message
  def print_message
    puts 'removing a limb'
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...