我试图通过自联接(基于@Shtééf's answer)在同一模型的记录之间实现多个关系。我有以下型号
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
通过此设置,我可以成功访问每个记录的下属和经理列表。但是,我很难以下列方式建立关系
e = Employee.create
e.subordinates.create
e.subordinates #=> []
e.managers.create
e.managers #=> []
问题是它没有设置关系类型,所以我必须写
e = Employee.create
s = Employee.create
e.relations.create employee_b: s, rel_type: 'manager of'
e.subordinates #=> [#<Employee id:...>]
我做错了吗?
答案 0 :(得分:8)
您可以在has_many关联上使用before_add
和before_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 }
这应该有效,并且您可以使用employe.managers.create
您可能希望在回调中使用build
create
的instread
您也可以阅读有关此解决方案的this question
答案 1 :(得分:3)
为了创建多个多对多自联接关联,我建议让多个表来管理连接更有意义。从数据的角度来看,从目前的确切情况来看,这一点非常清楚,从逻辑的角度来看也是如此。所以有这样的话:
create_table :manage_relation do |t|
t.references :employee_id
t.references :manager_id
end
create_table :subordinate_relation do |t|
t.references :employee_id
t.references :subordinate_id
end
class Employee < ActiveRecord::Base
has_many :subordinates,
:through => :subordinate_relation,
:class_name => "Employee",
:foreign_key => "subordinate_id"
has_many :managers,
:through => :manage_relation,
:class_name => "Employee",
:foreign_key => "manager_id"
belongs_to :employee,
:class_name => "Employee"
end
通过这种方式,从编码的角度来看,它不会比必要的更复杂,并且您可以使用标准集合访问它,它将适当地为您设置连接,而无需您管理它们。所以,这两个集合都应该有用..
employee.managers
employee.subordinates
您无需管理任何其他变量。合理?它增加了一个表格,但提高了清晰度。
答案 2 :(得分:2)
我会按如下方式重做你的模型:
class ManagerRelation < ActiveRecord::Base
belongs_to :manager, :class_name => 'Employee'
belongs_to :subordinate, :class_name => 'Employee'
end
class Employee < ActiveRecord::Base
has_many :manager_relations, :class_name => "ManagerRelation",
:foreign_key => :subordinate_id
has_many :subordinate_relations, :class_name => "ManagerRelation",
:foreign_key => :manager_id
has_many :managers, :source => :manager,
:through => :manager_relations
has_many :subordinates, :source => :subordinate,
:through => :subordinate_relations
end
现在您可以执行以下操作:
employee.managers
employee.subordinates
employee.managers << employee2
employee.subordinates << employee3
注意:当他们向两位经理报告时,通常会有一个人离开公司: - )
答案 3 :(得分:2)
鉴于呈现的关系
create_table :relations, force: true do |t|
t.references :employee_a
t.string :rel_type
t.references :employee_b
end
class Employee < ActiveRecord::Base
has_many :subordinate_relations, :class_name => "Relation", :conditions => {:rel_type => 'manager of'}, :foreign_key => :employee_a
has_many :subordinates, :through => :subordinate_relations, :source => :subordinate, :foreign_key => :employee_b
has_many :manager_relations, :class_name => "Relation", :conditions => {:rel_type => 'manager of'}, :foreign_key => :employee_b
has_many :managers, :through => :manager_relations, :source => :manager, :foreign_key => :employee_a
end
class Relation < ActiveRecord::Base
belongs_to :manager, :class_name => "Employee", :foreign_key => :employee_a
belongs_to :subordinate, :class_name => "Employee", :foreign_key => :employee_b
end
e = Employee.create
e.subordinates.create #Employee ...
e.subordinates #[<Employee ...]
e2 = Employee.create
e2.managers.create #Employee
e2.managers #[<Employee ...]
虽然解决方案有效 - 但通过将关联与“rel_type”联系起来,我有点困惑。在这种情况下 - 我会说rel_type是多余的,关系应该映射如下:
create_table :relations do |t|
t.reference :manager
t.reference :subordinate
end
在这种情况下,关联映射应该更简单。