我想在rails中创建自引用关系。我有一个人物模型,该人应该拥有具有相同Person对象的主人和学生。
到目前为止,我试过了:
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
当我尝试选择时似乎有效:
@a = Person.find(:first)
@a.masters
但是当我尝试推送到主人时,它会保存关系,而不会将状态设置为主人。它保存了null。当我进入主人时,是否有一种简单的方法可以保存status=master
;当我推入学生时,status=pupil
是否可以保存?{/ p>
由于
答案 0 :(得分:2)
简而言之,解决方案是:关联回调(更多来自关联回调部分:http://railsapi.com/doc/rails-v3.0.0/classes/ActiveRecord/Associations/ClassMethods.html)
为了更详细一些,我稍微调整了你的例子,但基本上结构是一样的,这里是代码:
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
RelationShip模型包含一个类型列,它等同于您的状态列,但如果我以后想要进行STI并声明MasterPupil / PupilMaster关系模型,则类型更好。
RelationShip还有一个set_type before_validation,它将类型设置为OpenRelationship,它应该是临时的,然后在每个关联中Person模型中定义的after_add回调将设置清楚(并设置MasterPupil或PupilMaster类型)
现在:
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">]