在单个继承表和另一个表中的类之间建立了许多关联

发布于 2025-02-01 02:10:19 字数 1008 浏览 2 评论 0 原文

我在部门和研究人员之间的许多关系中都有一个名为“关系”的联合表。

我希望能够通过做 decression.find(1).pudents 获得学生列表,但是我会得到 activerecord :: HasmanyThroughSourCeasSociationNotFoundError(找不到源协会: 。

模型研究员

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

I have a join table named Relations in a many to many relationship between departments and researchers.

I want to be able to get a list of students by doing Department.find(1).students but I am getting ActiveRecord::HasManyThroughSourceAssociationNotFoundError (Could not find the source association(s) :students in model Researcher. Try 'has_many :students, :through => :researchers, :source => <name>'.)

Why isn't it using the scope from the table Researcher?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

眼角的笑意。 2025-02-08 02:10:19

来源:选项期望关联作为参数。在内部,rails

# source: :students, through: :researchers
>> Researcher.reflect_on_association(:students)
=> nil

在修复 has_many之前:学生关联,要注意的几件事:

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

要解决关联,我们可以使用 scope> scope has_many 的参数/code>方法:

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

现在,我们需要修复关系之间的关联研究人员

# 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通过在关系>中定义其他关联来完成工作< /code>,无需范围:

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://api.rubyonrails.orgs.org/classes/classecoping/scoping/nemed/nemed/nemed/classml-classml-classml-w.metml#method-unpmetml#method-i-scope

https://api.rubyonrails.orgs.org/classes/classes/activerecord/activerecord/reflecord/reflection/reflection/classmethod/classmethods.htmlluctions.html#method-method-i-reflect_sass_sass_sassociation

source: option expects an association as argument. Internally, rails runs a reflection on the argument, like:

# source: :students, through: :researchers
>> Researcher.reflect_on_association(:students)
=> nil

Before fixing has_many :students association, a few things to note:

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

To fix the association we can use scope argument of has_many method:

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

Now, we need to fix the association between Relation and 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">]

You can also let rails do the work by defining additional associations in Relation, no scope required:

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

https://api.rubyonrails.org/classes/ActiveRecord/Reflection/ClassMethods.html#method-i-reflect_on_association

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文