Evite objetos huérfanos, con has_many a través de asociaciones, en Rails 3

I have a model with "member", "group", "membership" and "user". The data is organized in a tree structure with groups that have members associated to them. Members without any group associations are considered orphaned and is of no use for the application.

When a user destroys a group there should not be left any orphaned members. In other words: a member should be destroyed if and only if the last group association is removed. Preferably this should happen in a single transaction, but the most important aspect is that orphaned object is not accumulated in the database over time.

Only members and groups that are associated with the user should be removed. Groups, members and memberships owned by other users should not be affected at all. (One might argue that a global cleanup method could be run anytime, but I want to isolate destructive operations to only affect the current users objects.)

My question: What is the most efficient and elegant way to implement this functionality in Rails 3? My current implementation, illustrated by the simplified model described here, does not remove a member from the database unless the user manually deletes it (or that the entire user and all his/her data is removed by cascade deletion.)

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

preguntado el 01 de julio de 12 a las 09:07

1 Respuestas

I solved this by adding a callback in the Membership class, which notifies a Member when the membership is destroyed. The member object then destroys itself if it no longer has any group associations.

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

Respondido 27 Jul 12, 20:07

Worth noting: this will not work with a has_and_belongs_to_many association as the model calling after_destroy will no longer have an id and will not be able to determine which objects it used to be associated with. - YWCA Hola

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.