Ассоциация многих ко многим с множественными самолетами в Activerecord
-
28-10-2019 - |
Вопрос
Я пытаюсь внедрить несколько отношений между записями одной и той же модели через самостоятельные устройства (на основе @Ответ Штефа) У меня есть следующие модели
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:...>]
Я делаю что-то неправильно?
Решение
Вы можете использовать before_add
а также before_remove
обратный вызов в ассоциации HAS_MANY:
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
в обратном вызове
Также вы можете прочитать этот вопрос об этом решении
Другие советы
Чтобы создать множественную ассоциацию самообучения множества ко многим, я бы порекомендовал иметь больше смысла иметь несколько таблиц для управления соединением. Таким образом, с точки зрения данных очень ясно, что именно происходит, и это также ясно с логической точки зрения. Итак, что -то в этом направлении:
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
И вам не придется управлять любыми другими переменными. Иметь смысл? Это добавляет таблицу, но улучшает ясность.
Я бы переделал ваши модели следующим образом:
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
Примечание: Обычно это знак для того, чтобы покинуть компанию, когда они заставляются сообщить двум менеджерам :-)
Учитывая представленное отношение
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
В таком случае отображение ассоциации должно быть немного проще.