1

Railsで自己参照関係を作成したいと思います。私はPersonモデルを持っており、その人は同じ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に押し込んだり、生徒に押し込んだりするときに節約する簡単な方法はありますか?

ありがとう

4

1 に答える 1

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_typebefore_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">]
于 2010-09-10T23:39:59.357 に答える