Предотвращение потерянных объектов с has_many через ассоциации в Rails 3

У меня есть модель с «членом», «группой», «членством» и «пользователем». Данные организованы в виде древовидной структуры с группами, с которыми связаны члены. Члены без каких-либо групповых ассоциаций считаются потерянными и бесполезными для приложения.

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

Удалять следует только участников и группы, связанные с пользователем. Группы, участники и членства, принадлежащие другим пользователям, не должны быть затронуты вообще. (Можно возразить, что глобальный метод очистки может быть запущен в любое время, но я хочу изолировать деструктивные операции, чтобы они затрагивали только объекты текущих пользователей.)

Мой вопрос: каков наиболее эффективный и элегантный способ реализовать эту функциональность в Rails 3? Моя текущая реализация, проиллюстрированная упрощенной моделью, описанной здесь, не удаляет участника из базы данных, пока пользователь не удалит его вручную (или если весь пользователь и все его/ее данные будут удалены путем каскадного удаления).

class User < ActiveRecord::Base
  has_many :groups, :foreign_key => 'owner_id', :dependent => :delete_all
  has_many :members, :foreign_key => 'owner_id', :dependent => :destroy
end

class Member < ActiveRecord::Base
  belongs_to :owner, :class_name => 'User'    
  has_many :memberships, :dependent => :destroy
  has_many :groups, :through => :memberships
end

class Membership < ActiveRecord::Base
  belongs_to :member
  belongs_to :group
end

class Group < ActiveRecord::Base
  belongs_to :owner, :class_name => 'User'
  belongs_to :parent, :class_name => 'Group'    
  has_many :groups, :foreign_key => 'parent_id', :dependent => :destroy
  has_many :memberships, :dependent => :destroy
  has_many :members, :through => :memberships
end

person Lars Preben Sørsdahl    schedule 01.07.2012    source источник


Ответы (1)


Я решил эту проблему, добавив обратный вызов в класс Membership, который уведомляет участника об уничтожении членства. Затем объект-член уничтожает себя, если у него больше нет групповых ассоциаций.

class Membership < ActiveRecord::Base
  belongs_to :member
  belongs_to :group

  after_destroy :notify_member

  def notify_member
    member.destroy_if_empty_groups
  end
end

class Member < ActiveRecord::Base
  belongs_to :owner, :class_name => 'User'    
  has_many :memberships, :dependent => :destroy
  has_many :groups, :through => :memberships

  def destroy_if_empty_groups
    if groups.count == 0
      self.destroy
    end
  end
end
person Lars Preben Sørsdahl    schedule 27.07.2012
comment
Стоит отметить: это не будет работать с ассоциацией has_and_belongs_to_many, так как модель, вызывающая after_destroy, больше не будет иметь id и не сможет определить, с какими объектами она раньше была связана. - person YWCA Hello; 02.05.2013