1

Je voudrais créer une relation d'auto-référencement dans les rails. J'ai un modèle Personne, et la personne devrait avoir des maîtres et des élèves avec le même objet Personne.Utilisation de la relation d'auto-référencement polymorphe plusieurs-à-plusieurs avec les attributs de la relation dans les rails

Jusqu'à présent, j'ai essayé:

class Person <ActiveRecord::Base 
    has_many :relationships, :dependent => :destroy 
    has_many :masters, :through => :relationships, :conditions => "status='master'" 
    has_many :pupils, :through => :relationships, :conditions => "status='pupil'" 
    has_many :inverse_relationships, :class_name => "Relationship", 
     :foreign_key => "related_id" 
    has_many :inverse_masters, :through => :inverse_relationships, 
     :source => :person, :conditions => "status='master'" 
    has_many :inverse_pupils, :through => :inverse_relationships, 
     :source => :person, :conditions => "status='pupil'" 
end 

class Relationship < ActiveRecord::Base 
    belongs_to :person 
    belongs_to :master, :class_name => "Person", :foreign_key => 'related_id' 
    belongs_to :pupil, :class_name => "Person", :foreign_key => 'related_id' 
end 

Il semble fonctionner quand je suis en train de sélectionner:

@a = Person.find(:first) 
@a.masters 

mais lorsque je tente de faire une poussée en maîtres, il sauve la relation sans l'état défini sur maître. Il économise null à la place. Y at-il un moyen facile d'enregistrer status=master quand je pousse dans les maîtres et status=pupil quand je pousse dans les élèves?

Merci

Répondre

2

Pour faire court, la solution est: callbacks association (plus ici sous l'Association section Callback: http://railsapi.com/doc/rails-v3.0.0/classes/ActiveRecord/Associations/ClassMethods.html)

Pour moi un peu plus détaillées ont adapté votre exemple un peu, mais essentiellement la structure est la même, voici le code:

class Person < ActiveRecord::Base 
    has_many :relationships 
    has_many :pupils, :through => :relationships, :source => :other_person, :conditions => 'relationships.type = "MasterPupil"', :after_add => Proc.new{|p,o| Relationship.update_all("type = 'MasterPupil'", ['person_id = ? AND other_person_id = ?', p.id, o.id])} 
    has_many :masters, :through => :relationships, :source => :other_person, :conditions => 'relationships.type = "PupilMaster"', :after_add => Proc.new{|p,o| Relationship.update_all("type = 'PupilMaster'", ['person_id = ? AND other_person_id = ?', p.id, o.id])} 
end 

class Relationship < ActiveRecord::Base 
    belongs_to :person 
    belongs_to :other_person, :class_name => 'Person' 
    before_validation :set_type 

    def set_type 
    self.type = 'OpenRelationship' 
    end 
end 

class MasterPupil < Relationship 
end 

class PupilMaster < Relationship 
end 

le modèle RelationShip contient une colonne de type qui est l'équivalent de votre colonne d'état, mais le type est plus agréable si je veux faire plus tard une ITS d déclare des modèles de relations MasterPupil/PupilMaster.

RelationShip dispose également d'un before_validation de set_type qui définirait le type de relation libre qui devrait être temporaire avant le rappel after_add défini dans le modèle de personne dans chaque association définira les choses claires (et définir soit un type MasterPupil ou PupilMaster)

et maintenant:

Loading development environment (Rails 3.0.0) 
irb(main):001:0> p = Person.create 
=> #<Person id: 1, created_at: "2010-09-10 23:35:37", updated_at: "2010-09-10 23:35:37"> 
irb(main):002:0> p.pupils 
=> [] 
irb(main):003:0> p.masters 
=> [] 
irb(main):004:0> p.pupils << Person.create 
=> [#<Person id: 2, created_at: "2010-09-10 23:35:56", updated_at: "2010-09-10 23:35:56">] 
irb(main):005:0> Relationship.all 
=> [#<MasterPupil id: 1, person_id: 1, other_person_id: 2, type: "MasterPupil">] 
irb(main):006:0> p.masters << Person.create 
=> [#<Person id: 3, created_at: "2010-09-10 23:36:29", updated_at: "2010-09-10 23:36:29">] 
irb(main):007:0> Relationship.all 
=> [#<MasterPupil id: 1, person_id: 1, other_person_id: 2, type: "MasterPupil">, #<PupilMaster id: 2, person_id: 1, other_person_id: 3, type: "PupilMaster">] 
irb(main):008:0> p.reload 
=> #<Person id: 1, created_at: "2010-09-10 23:35:37", updated_at: "2010-09-10 23:35:37"> 
irb(main):009:0> p.pupils 
=> [#<Person id: 2, created_at: "2010-09-10 23:35:56", updated_at: "2010-09-10 23:35:56">] 
irb(main):010:0> p.masters 
=> [#<Person id: 3, created_at: "2010-09-10 23:36:29", updated_at: "2010-09-10 23:36:29">] 
+0

C'est ce que je pensais, je vais avoir besoin de rails 3 pour cela. Comment rétrocompatible est r3? Bien que je ne puisse probablement rien perdre avec une bonne sauvegarde;) J'ai essayé cette solution mais j'ai 2.3.8 et il n'y a pas encore les rappels d'association. J'ai vérifié le valid_keywords dans la source pour cela;) Merci, j'y reviendrai demain, mais je suppose que c'est parfait, comme je l'ai essayé, seule la version ne correspond pas. – Tamisoft

+0

OK, il semble que cela fonctionne parfaitement sur 2.3.8 aussi, bien que certains ont dit sur les forums qu'il pourrait avoir des bugs, mais pour l'instant je suis prêt. Merci beaucoup pour l'aide – Tamisoft

+0

aucun problème. il suffit de voter pour ma réponse et ce sera génial :) – hellvinz