与ActiveRecord中的多个自联接进行多对多关联

And*_*rei 8 activerecord many-to-many ruby-on-rails associations self-join

我试图通过自连接实现同一模型的记录之间的多个关系(基于@Shtééf的答案).我有以下型号

create_table :relations, force: true do |t|
  t.references :employee_a
  t.string     :rel_type
  t.references :employee_b
end

class Relation < ActiveRecord::Base
  belongs_to :employee_a, :class_name => 'Employee'
  belongs_to :employee_b, :class_name => 'Employee'
end

class Employee < ActiveRecord::Base
  has_many :relations, foreign_key: 'employee_a_id'
  has_many :reverse_relations, class_name: 'Relation', foreign_key: 'employee_b_id'

  has_many :subordinates, through: :relations, source: 'employee_b', conditions: {'relations.rel_type' => 'manager of'}
  has_many :managers, through: :reverse_relations, source: 'employee_a', conditions: {'relations.rel_type' => 'manager of'}
end
Run Code Online (Sandbox Code Playgroud)

通过此设置,我可以成功访问每个记录的下属和管理员列表.但是,我很难以下列方式建立关系

e = Employee.create
e.subordinates.create
e.subordinates #=> []
e.managers.create
e.managers #=> []
Run Code Online (Sandbox Code Playgroud)

问题是它没有设置关系类型,所以我必须写

e = Employee.create
s = Employee.create
e.relations.create employee_b: s, rel_type: 'manager of'
e.subordinates #=> [#<Employee id:...>]
Run Code Online (Sandbox Code Playgroud)

难道我做错了什么?

Adr*_*uio 8

您可以在has_many关联上使用before_addbefore_remove回调:

class Employee < ActiveRecord::Base
  has_many :relations, foreign_key: 'employee_a_id'
  has_many :reverse_relations, class_name: 'Relation', foreign_key: 'employee_b_id'

  has_many :subordinates, 
           through: :relations, 
           source: 'employee_b', 
           conditions: {'relations.rel_type' => 'manager of'}
           :before_add => Proc.new { |employe,subordinate| employe.relations.create(employe_b: subordinate, rel_type: 'manager of') },
           :before_remove => Proc.new { |employe,subordinate| employe.relations.where(employe_b: subordinate, rel_type: 'manager of').first.destroy }

  has_many :managers,  
           through: :reverse_relations, 
           source: 'employee_a', 
           conditions: {'relations.rel_type' => 'manager of'}
           :before_add => Proc.new { |employe,manager| employe.reverse_relations.create(employe_a: manager, rel_type: 'manager of') },
           :before_remove => Proc.new { |employe,manager| employe.reverse_relations.where(employe_b: subordinate, rel_type: 'manager of').first.destroy }
Run Code Online (Sandbox Code Playgroud)

这应该工作,使你能够使用employe.managers.create
你可能想在回调中使用buildinstread 也你可以阅读有关此解决方案的这个问题create