обратные вызовы для активных ассоциаций записей

У меня есть модель утверждения отпуска, в которой has_many :entries есть ли способ, чтобы, если я уничтожу одну из этих записей, остальные были уничтожены? Я также хочу отправить одно электронное письмо, если они есть, но не по одному для каждой записи. Есть ли способ наблюдать за изменениями в коллекции в целом?


person loosecannon    schedule 23.06.2011    source источник


Ответы (5)


Обратный вызов, вероятно, не является хорошим выбором, потому что:

class Entry < ActiveRecord::Base
  def after_destroy
    Entry.where(:vacation_id => self.vacation_id).each {|entry| entry.destroy}
  end
end

приведет к плохой рекурсии.

Возможно, вы должны сделать это в контроллере:

class EntriesController < ApplicationController
  def destroy
    @entry = Entry.find(params[:id])
    @entries = Entry.where(:vacation_id => @entry.vacation_id).each {|entry| entry.destroy}
    #send email here
    ...
  end
end
person tybro0103    schedule 23.06.2011
comment
или, возможно, @entry.vacation.entries.destroy_all было бы чище - person tybro0103; 24.06.2011
comment
да, я думаю, мне нужно сделать это в контроллере и просто удалить родительский VacationApproval, установленный на :depoendent => destroy - person loosecannon; 24.06.2011

Вы можете использовать обратный вызов before_destroy.

class VacationRequest < ActiveRecord::Base
  has_many :entries
end

class Entry < ActiveRecord::Base
  belongs_to :vacation_request
  before_destroy :destroy_others
  def destroy_others
    self.vacation_request.entries.each do |e|
      e.mark_for_destruction unless e.marked_for_destruction?
    end
  end
end

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

person Luke    schedule 23.06.2011
comment
я думал об этом, но разве вызов entry.destroy_all call before_destroy для каждого из них просто не привел бы к бесконечной рекурсии? - person loosecannon; 24.06.2011
comment
Отредактировано с (надеюсь) лучшей стратегией. - person Luke; 24.06.2011
comment
в документах сказано, что mark_for_destruction полезен только с :autosave, так что я попробую, когда увижу, что происходит с :dependent =› destroy - person loosecannon; 24.06.2011
comment
Да, хороший звонок :autosave. Однако будьте осторожны, когда ставите :dependent => :destroy. Вы хотите, чтобы сам VacationRequest уничтожался всякий раз, когда уничтожается любая из его записей? - person Luke; 24.06.2011

Я думаю, что это должно работать:

class Entry < ActiveRecord::Base
  belongs_to :vacation_request, :dependent => :destroy

  # ...
end

class VacationApproval < ActiveRecord::Base
  has_many :entries, :dependent => :destroy

  # ...
end

Что должно произойти, так это то, что при уничтожении Записи связанный с ней VacationApproval будет уничтожен, а впоследствии все связанные с ним Записи будут уничтожены.

Дайте мне знать, если это работает для вас.

person Jordan Running    schedule 23.06.2011
comment
у меня это в VacationApproval, а не в Entry.. Что делать, если есть запись, которая не принадлежит ни одной? будет ли ему позволено существовать. Лишь немногие действительно имеют разрешение на отпуск, связанное с ними. - person loosecannon; 24.06.2011
comment
Я бы хотел, чтобы это сработало, но :зависимый — это только опция has_many, а не own_to (по крайней мере, в Rails 2.3) - person loosecannon; 24.06.2011
comment
Хороший улов, свободная пушка. Однако похоже, что некоторые другие ответы решат вашу проблему. - person Jordan Running; 24.06.2011

Итак, что я в итоге сделал, это

class VacationApproval < ActiveRecord::Base
  has_many :entries , :conditions => {:job_id => Job.VACATION.id }, :dependent => :nullify

class Entry < ActiveRecord::Base
  validates_presence_of :vacation_approval_id ,:if => lambda {|entry| entry.job_id == Job.VACATION.id} , :message => "This Vacation Has Been Canceled. Please Delete These Entries."

а потом

@entries.each {|entry| entry.destroy if entry.invalid? }

в индексном действии моего контроллера. и

`raise "Entries are not valid, please check them and try again ( Did you cancel your vacation? )" if @entries.any? &:invalid?` 

в действии отправки

Проблема с одновременным удалением других заключается в том, что мой пользовательский интерфейс делает 10 вызовов Ajax для выбора 10 строк и удаляет их все в первый раз, когда я получаю 9 необработанных ответов 404, что было нежелательно.

Поскольку меня это не волнует, они остаются там, пока Заявка не может быть отправлена, все в порядке.

Это был самый простой/безопасный/дружественный к рекурсии способ для меня, но, вероятно, не лучший способ. Спасибо за вашу помощь!

person loosecannon    schedule 24.06.2011

Кому интересно/ищу информацию

В итоге я решил это позже, настроив модель APProval для отпуска следующим образом:

class VacationApproval < ActiveRecord::Base
    has_many :entries , :conditions => {:job_id => Job.VACATION.id }, :dependent => :delete_all
end

и моя модель входа, как это

class Entry < ActiveRecord::Base  
  after_destroy :cancel_vacation_on_destory
  def cancel_vacation_on_destory
    if !self.vacation_approval.nil?
      self.vacation_approval.destroy
    end
  end
end

Использование :delete_all не обрабатывает обратные вызовы, а просто удаляет их.

person loosecannon    schedule 13.07.2011