Ruby on rails 多目标通过以下方式实现:

Ruby on rails 多目标通过以下方式实现:,ruby-on-rails,database,activerecord,Ruby On Rails,Database,Activerecord,我有三种型号: User(name: string) Course(name: string) Assoc(user_id: integer, ta_id: integer, teach_id: integer, student_id: integer) 我希望能够将用户与课程相关联,例如助教、教师或学生。我不确定这是否符合ActiveRecord中的:多态思想。以下是我目前掌握的情况,但它不太管用: class User < ApplicationRecord has_many :

我有三种型号:

User(name: string)
Course(name: string)
Assoc(user_id: integer, ta_id: integer, teach_id: integer, student_id: integer)
我希望能够将用户与课程相关联,例如助教、教师或学生。我不确定这是否符合ActiveRecord中的:多态思想。以下是我目前掌握的情况,但它不太管用:

class User < ApplicationRecord
  has_many :tas, class_name: "Assoc", foreign_key: :ta_id
  has_many :teaches, class_name: "Assoc", foreign_key: :teach_id
  has_many :takes, class_name: "Assoc", foreign_key: :student_id

  has_many :ta_courses, through: :tas
  has_many :taken_courses, through: :tas
  has_many :taught_courses, through: :tas
end
class用户
它不起作用:

irb(main):056:0> User.find(1).ta_courses
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
Traceback (most recent call last):
        1: from (irb):56
ActiveRecord::HasManyThroughSourceAssociationNotFoundError (Could not find the source association(s) "ta_course" or :ta_courses in model Assoc. Try 'has_many :ta_courses, :through => :tas, :source => <name>'. Is it one of ?)
irb(main):057:0>
irb(main):056:0>User.find(1).ta_课程
用户加载(0.1ms)选择“用户”。*从“用户”中选择“用户”。“id”=$1限制$2[[“id”,1],“限制”,1]]
回溯(最近一次呼叫最后一次):
1:from(irb):56
ActiveRecord::HasManyThroughSourceAssociationNotFoundError(在模型Assoc中找不到源关联“ta_课程”或:ta_课程。请尝试“有多个:ta_课程,:通过=>:tas,:source=>”。它是其中之一吗?)
irb(主要):057:0>

任何提示都将不胜感激

看起来您还没有建立从Assoc模型到课程模型的关系。您的Assoc模型应该有一个课程id,因此它看起来像:

Assoc(user_id: integer, ta_id: integer, teach_id: integer, student_id: integer, course_id: integer)
然后,您需要在Assoc模型上建立“属于”关系:

class Assoc < ApplicationRecord
  belongs_to :course
end
class Assoc
最后,用户模型中的许多直通关系没有正确建立(都是通过TA建立的)。错误消息为您提供了最后一件需要做的事情的线索,即使用源标识别名关系:

class User < ApplicationRecord
  has_many :tas, class_name: "Assoc", foreign_key: :ta_id
  has_many :teaches, class_name: "Assoc", foreign_key: :teach_id
  has_many :takes, class_name: "Assoc", foreign_key: :student_id

  has_many :ta_courses, through: :tas, source: :course
  has_many :taught_courses, through: :teaches, source: :course
  has_many :taken_courses, through: :takes, source: :course
end
class用户
看起来您还没有建立从Assoc模型到课程模型的关系。您的Assoc模型应该有一个课程id,因此它看起来像:

Assoc(user_id: integer, ta_id: integer, teach_id: integer, student_id: integer, course_id: integer)
然后,您需要在Assoc模型上建立“属于”关系:

class Assoc < ApplicationRecord
  belongs_to :course
end
class Assoc
最后,用户模型中的许多直通关系没有正确建立(都是通过TA建立的)。错误消息为您提供了最后一件需要做的事情的线索,即使用源标识别名关系:

class User < ApplicationRecord
  has_many :tas, class_name: "Assoc", foreign_key: :ta_id
  has_many :teaches, class_name: "Assoc", foreign_key: :teach_id
  has_many :takes, class_name: "Assoc", foreign_key: :student_id

  has_many :ta_courses, through: :tas, source: :course
  has_many :taught_courses, through: :teaches, source: :course
  has_many :taken_courses, through: :takes, source: :course
end
class用户
如果我真的必须能够将所有用户->课程关系作为一个表进行查询,我会将其设置为:

# rails g model assoc user:belongs_to course:belongs_to
class Assoc < ApplicationRecord
  enum role: [:student, :teacher, :assistent]
  belongs_to :user
  belongs_to :course
end
由于我们希望在这两个模块中具有完全相同的关联,因此让我们使用模块保持干燥:

app/models/concerns/associated.rb
# dynamically creates assocations for each role in Assoc.roles enum
module Associated
  extend ActiveSupport::Concern
  included do
    # the plain base assocation
    has_many :assocs
    # this creates the assocations student_assocs, teacher_assocs, etc
    Assoc.roles.keys.each do |role|
      # We need to use eval for the second argument as we are creating the lambda dynamically
      has_many :"#{role}_assocs", eval( "->{ where(role: #{Assoc.roles[role]})}" ),
        class_name: 'Assoc'
    end
  end
end
Assoc.roles
给出了我们在Assoc中设置的枚举映射的哈希值

然后,我们可以在课程和用户中包含我们的模块,并设置间接关联:

class Course < ApplicationRecord
  include Associated
  has_many :users, through: :assocs
  # this creates the assocations students, teachers, etc
  Assoc.roles.keys.each do |role|
    has_many role.pluralize.to_sym,
      through: "#{role}_assocs".to_sym,
      source: :user
  end
end

class User < ApplicationRecord
  include Associated
  has_many :courses, through: :assocs
   # this creates the assocations courses_as_student, courses_as_teacher, etc
  Assoc.roles.keys.each do |role|
    has_many "course_as_#{role}".to_sym,
      through: "#{role}_assocs".to_sym,
      source: :course
  end
end
课程
如果我真的必须能够将所有用户->课程关系作为一个表进行查询,我会将其设置为:

# rails g model assoc user:belongs_to course:belongs_to
class Assoc < ApplicationRecord
  enum role: [:student, :teacher, :assistent]
  belongs_to :user
  belongs_to :course
end
由于我们希望在这两个模块中具有完全相同的关联,因此让我们使用模块保持干燥:

app/models/concerns/associated.rb
# dynamically creates assocations for each role in Assoc.roles enum
module Associated
  extend ActiveSupport::Concern
  included do
    # the plain base assocation
    has_many :assocs
    # this creates the assocations student_assocs, teacher_assocs, etc
    Assoc.roles.keys.each do |role|
      # We need to use eval for the second argument as we are creating the lambda dynamically
      has_many :"#{role}_assocs", eval( "->{ where(role: #{Assoc.roles[role]})}" ),
        class_name: 'Assoc'
    end
  end
end
Assoc.roles
给出了我们在Assoc中设置的枚举映射的哈希值

然后,我们可以在课程和用户中包含我们的模块,并设置间接关联:

class Course < ApplicationRecord
  include Associated
  has_many :users, through: :assocs
  # this creates the assocations students, teachers, etc
  Assoc.roles.keys.each do |role|
    has_many role.pluralize.to_sym,
      through: "#{role}_assocs".to_sym,
      source: :user
  end
end

class User < ApplicationRecord
  include Associated
  has_many :courses, through: :assocs
   # this creates the assocations courses_as_student, courses_as_teacher, etc
  Assoc.roles.keys.each do |role|
    has_many "course_as_#{role}".to_sym,
      through: "#{role}_assocs".to_sym,
      source: :course
  end
end
课程我会考虑如果把所有的东西聚到一个单一的连接模型/表格中,那真是个好主意。您可以用几个离散的模型/表完成同样的事情,避免创建god类,更重要的是,您可以使用更好的索引。不,你所做的不是polymorpism——ActiveRecord中的多态性是通过使用id和类型列动态地连接一个表。你认为哪一个是god类?用户?我看到的两种选择似乎都更复杂:在用户、三种用户周围有实际的多态性。或者,有三个单独的联接表。确实,这两种方法都比较容易实现,但似乎不必要地复杂。Assoc是god类,因为它将所有内容链接在一起,并且没有单一的责任。单表继承并不是真正的多态性。它只是使用相同的机制-类型列。多态性是