在单继承 table 中的 类 和另一个 table 之间建立多对多关联
Set up many to many associations between classes in single inheritance table and another table
我在部门和研究人员之间的多对多关系中有一个名为 Relations 的联接 table。
我希望能够通过 Department.find(1).students
获取学生列表,但我得到的是 ActiveRecord::HasManyThroughSourceAssociationNotFoundError (Could not find the source association(s) :students in model Researcher. Try 'has_many :students, :through => :researchers, :source => <name>'.)
为什么不使用 table 研究员的瞄准镜?
class Department < ApplicationRecord
has_many :relations
has_many :researchers, through: :relations
has_many :students, source: :students, through: :researchers
has_many :advisors, source: :advisors, through: :researchers
end
class Relation < ApplicationRecord
belongs_to :researcher
belongs_to :department
end
class Reseacher < ApplicationRecord
scope :students, -> { where(type: 'Student') }
scope :advisors, -> { where(type: 'Advisor') }
end
class Student < Researcher
has_many :relations, foreign_key: :department_id
has_many :departments, through: :relations
end
class Advisor < Researcher
has_many :relations, foreign_key: :department_id
has_many :departments, through: :relations
end
source:
选项需要一个关联作为参数。在内部,rails runs 对参数的反思,例如:
# source: :students, through: :researchers
>> Researcher.reflect_on_association(:students)
=> nil
修复has_many :students关联之前,有几点需要注意:
has_many :students, # will look for `students` association in the intermediate
# model unless source is specified; intermediate model is
# determined by reflecting on through option `:researchers`
#
# reflect_on_association(:researchers).klass # => Researcher
through: :researchers # can't go through `researchers`; already there.
# `Student` is a `Researcher`.
source: :students, # there is no `students` association in `Researcher` class.
#
# reflect_on_association(:researchers).klass
# .reflect_on_association(:students) # => nil
要修复关联,我们可以使用 has_many
方法的 scope
参数:
has_many(name, scope = nil, **options, &extension)
# ^ pass a proc as a second argument
class Department < ApplicationRecord
# NOTE: add `dependent: :destroy` option to destroy corresponding Relations
# when destroying a Department
has_many :relations, dependent: :destroy
has_many :researchers, through: :relations
has_many :students,
-> { where(type: "Student") }, # scope the associated model
through: :relations, # relevant association is in Relation model
source: :researcher # look for `researcher` association in Relation.
# instead of `student`
# NOTE: use existing scope from another model
has_many :advisors,
-> { advisors }, # this runs in the source class.
through: :relations, # |
source: :researcher # <------------------'
# Researcher has `advisors` class method,
# defined by `scope: :advisors`.
end
现在,我们需要修复 Relation
和 Researcher
之间的关联:
# NOTE: what if you need another "relation" class to make another many-to-many association.
# TODO: call this something a bit more descriptive like `DepartmentStaff`
# or use the conventional `DepartmentResearcher`.
class Relation < ApplicationRecord
belongs_to :researcher
belongs_to :department
end
class Researcher < ApplicationRecord
scope :students, -> { where(type: "Student") }
scope :advisors, -> { where(type: "Advisor") }
# NOTE: `has_many :relations` is the opposite of `belongs_to :researcher`
# `foreign_key` is `researcher_id` which is the default and
# should not be changed.
# has_many :relations, foreign_key: :department_id
has_many :relations, dependent: :destroy # <--.
has_many :departments, through: :relations # <--|
end # |
# |
class Student < Researcher # |
# NOTE: no need to duplicate these; put it in the parent class.
# has_many :relations
# has_many :departments, through: :relations
end
class Advisor < Researcher
end
>> Relation.create!([{researcher: Student.new, department: Department.create},{researcher: Advisor.new, department: Department.first}])
>> Department.first.students
=> [#<Student:0x00007f7f78ae5f98 id: 1, type: "Student">]
>> Department.first.advisors
=> [#<Advisor:0x00007f7f789a9b20 id: 2, type: "Advisor">]
>> Department.first.researchers
=> [#<Student:0x00007f7f786bdc90 id: 1, type: "Student">, #<Advisor:0x00007f7f786bd858 id: 2, type: "Advisor">]
您也可以让 rails 通过在 Relation
中定义额外的关联来完成工作,不需要范围:
class Relation < ApplicationRecord
belongs_to :researcher
belongs_to :department
belongs_to :student, foreign_key: :researcher_id, optional: true
belongs_to :advisor, foreign_key: :researcher_id, optional: true
end
class Department < ApplicationRecord
has_many :relations, dependent: :destroy
has_many :researchers, through: :relations
has_many :students, through: :relations
has_many :advisors, through: :relations
end
https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
https://api.rubyonrails.org/classes/ActiveRecord/Scoping/Named/ClassMethods.html#method-i-scope
https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many
我在部门和研究人员之间的多对多关系中有一个名为 Relations 的联接 table。
我希望能够通过 Department.find(1).students
获取学生列表,但我得到的是 ActiveRecord::HasManyThroughSourceAssociationNotFoundError (Could not find the source association(s) :students in model Researcher. Try 'has_many :students, :through => :researchers, :source => <name>'.)
为什么不使用 table 研究员的瞄准镜?
class Department < ApplicationRecord
has_many :relations
has_many :researchers, through: :relations
has_many :students, source: :students, through: :researchers
has_many :advisors, source: :advisors, through: :researchers
end
class Relation < ApplicationRecord
belongs_to :researcher
belongs_to :department
end
class Reseacher < ApplicationRecord
scope :students, -> { where(type: 'Student') }
scope :advisors, -> { where(type: 'Advisor') }
end
class Student < Researcher
has_many :relations, foreign_key: :department_id
has_many :departments, through: :relations
end
class Advisor < Researcher
has_many :relations, foreign_key: :department_id
has_many :departments, through: :relations
end
source:
选项需要一个关联作为参数。在内部,rails runs 对参数的反思,例如:
# source: :students, through: :researchers
>> Researcher.reflect_on_association(:students)
=> nil
修复has_many :students关联之前,有几点需要注意:
has_many :students, # will look for `students` association in the intermediate
# model unless source is specified; intermediate model is
# determined by reflecting on through option `:researchers`
#
# reflect_on_association(:researchers).klass # => Researcher
through: :researchers # can't go through `researchers`; already there.
# `Student` is a `Researcher`.
source: :students, # there is no `students` association in `Researcher` class.
#
# reflect_on_association(:researchers).klass
# .reflect_on_association(:students) # => nil
要修复关联,我们可以使用 has_many
方法的 scope
参数:
has_many(name, scope = nil, **options, &extension)
# ^ pass a proc as a second argument
class Department < ApplicationRecord
# NOTE: add `dependent: :destroy` option to destroy corresponding Relations
# when destroying a Department
has_many :relations, dependent: :destroy
has_many :researchers, through: :relations
has_many :students,
-> { where(type: "Student") }, # scope the associated model
through: :relations, # relevant association is in Relation model
source: :researcher # look for `researcher` association in Relation.
# instead of `student`
# NOTE: use existing scope from another model
has_many :advisors,
-> { advisors }, # this runs in the source class.
through: :relations, # |
source: :researcher # <------------------'
# Researcher has `advisors` class method,
# defined by `scope: :advisors`.
end
现在,我们需要修复 Relation
和 Researcher
之间的关联:
# NOTE: what if you need another "relation" class to make another many-to-many association.
# TODO: call this something a bit more descriptive like `DepartmentStaff`
# or use the conventional `DepartmentResearcher`.
class Relation < ApplicationRecord
belongs_to :researcher
belongs_to :department
end
class Researcher < ApplicationRecord
scope :students, -> { where(type: "Student") }
scope :advisors, -> { where(type: "Advisor") }
# NOTE: `has_many :relations` is the opposite of `belongs_to :researcher`
# `foreign_key` is `researcher_id` which is the default and
# should not be changed.
# has_many :relations, foreign_key: :department_id
has_many :relations, dependent: :destroy # <--.
has_many :departments, through: :relations # <--|
end # |
# |
class Student < Researcher # |
# NOTE: no need to duplicate these; put it in the parent class.
# has_many :relations
# has_many :departments, through: :relations
end
class Advisor < Researcher
end
>> Relation.create!([{researcher: Student.new, department: Department.create},{researcher: Advisor.new, department: Department.first}])
>> Department.first.students
=> [#<Student:0x00007f7f78ae5f98 id: 1, type: "Student">]
>> Department.first.advisors
=> [#<Advisor:0x00007f7f789a9b20 id: 2, type: "Advisor">]
>> Department.first.researchers
=> [#<Student:0x00007f7f786bdc90 id: 1, type: "Student">, #<Advisor:0x00007f7f786bd858 id: 2, type: "Advisor">]
您也可以让 rails 通过在 Relation
中定义额外的关联来完成工作,不需要范围:
class Relation < ApplicationRecord
belongs_to :researcher
belongs_to :department
belongs_to :student, foreign_key: :researcher_id, optional: true
belongs_to :advisor, foreign_key: :researcher_id, optional: true
end
class Department < ApplicationRecord
has_many :relations, dependent: :destroy
has_many :researchers, through: :relations
has_many :students, through: :relations
has_many :advisors, through: :relations
end
https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
https://api.rubyonrails.org/classes/ActiveRecord/Scoping/Named/ClassMethods.html#method-i-scope
https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many