【问题标题】:Building an active record query with multiple joins构建具有多个连接的活动记录查询
【发布时间】:2017-10-21 02:27:15
【问题描述】:

我正在尝试了解 ActiveRecord 查询。来自 sql 背景,这有点令人困惑。我正在尝试编写一个查询来获取特定学生注册的所有课程,并使用以下查询:

def self.courses_enrolled_in(student_id)
    Course.joins(:sections).where(sections: {enrollment: Enrollment.where(student: Student.find_by(id: student_id))})
  end

但是 rails 控制台给出以下错误:

ActiveRecord::StatementInvalid: SQLite3::SQLException: 没有这样的列:sections.enrollment:

这是从以前的asked question 拼凑而成,从内到外。我明白错误在说什么,但我想我不清楚如何像在 sql 中一样将连接链接在一起以将一个表连接到另一个表。我将如何编写查询来提取特定学生注册的所有课程?

模型

注册

class Enrollment < ApplicationRecord
  belongs_to :section
  belongs_to :student
end

学生:

class Student < ApplicationRecord
  has_many :enrollments
end

教授:

class Section < ApplicationRecord
  has_many :enrollments
  belongs_to :professor
  belongs_to :course

end

课程:

class Course < ApplicationRecord

  belongs_to :department
  has_many :sections
  has_many :professors, through: :sections
end

【问题讨论】:

    标签: ruby-on-rails activerecord


    【解决方案1】:

    在 where 函数中指定连接条件时,应指定表名而不是关联名。因此,您的示例可能需要使用“注册”。 Active Record Query Interface Rails 指南应该可以帮助您理解使用 Active Record 进行查询

    【讨论】:

      【解决方案2】:

      那么看起来您要问的是,鉴于学生注册了许多课程的许多部分,该学生注册了哪些课程?

      我们将忽略教授,因为这与原始问题无关。

      或者换一种说法

      学生 1:M 注册 M:1 部分 M:1 课程

      或学生 M:M 部分。 Enrollments 是 Student 和 Section 之间 M:M 关系的联接表,不是吗?

      所以我们马上看到学生应该是:

      rails g student name
      class Student < ApplicationRecord
        has_many :enrollments
        has_many :sections, through: :enrollments
      end
      

      Enrollments 是一个连接表,所以它需要学生和部门的 ID

      rails g enrollment student_id section_id
      class Enrollment < ApplicationRecord
        belongs_to :student
        belongs_to :section
      end
      

      (仅当您想要使用 Enrollment 实例并使用它的关系时才需要定义所属)

      部分

      rails g section name course_id
      class Section < ApplicationRecord
        has_many :enrollments
        has_many :students, through: :enrollments
        belongs_to :course
      end
      

      (同样,has_manys 仅在您想让学生注册某个部分时才需要)

      还有课程

      rails g course name
      class Course < ApplicationRecord
        has_many :sections
      end
      

      当把它放在一起时,我们想要使用 Student 的实例(获取特定学生注册的所有课程),因此 self.courses_enrolled_in(student_id) 语句是错误的处理方式。您应该可以在 Rails 控制台中尝试这一切,因此您可以从获取学生开始:

      (为简洁起见,从控制台输出中省略了时间戳)

      [9] pry(main)> student = Student.first
        Student Load (13.8ms)  SELECT  `students`.* FROM `students` ORDER BY `students`.`id` ASC LIMIT 1
      => #<Student:0x007fc40fb88b68
       id: 1,
       name: "Fred",
      

      现在我们有了学生,问它有哪些部分:

      pry(main)> sections = student.sections
        Section Load (14.1ms)  SELECT `sections`.* FROM `sections` INNER JOIN `enrollments` ON `sections`.`id` = `enrollments`.`section_id` WHERE `enrollments`.`student_id` = '1'
      => [#<Section:0x007fc40c474208
        id: 1,
        name: "1st period",
        course_id: "1",
       #<Section:0x007fc40c474028
        id: 5,
        name: "2nd period",
        course_id: "2",
       #<Section:0x007fc40c46fe60
        id: 9,
        name: "3rd period",
        course_id: "3",
      ]
      

      对于每个部分,我们都可以获得课程:

      pry(main)> sections.first.course
        Course Load (17.1ms)  SELECT  `courses`.* FROM `courses` WHERE `courses`.`id` = 1 LIMIT 1
      => #<Course:0x007fc40f756a18
       id: 1,
       name: "English",
      

      这将为集合中的每个结果运行单独的查询,而不是最佳的,因此我们可以使用包含进行急切加载:

      pry(main)> student.sections.includes(:course)
            Section Load (5.9ms)  SELECT `sections`.* FROM `sections` INNER JOIN `enrollments` ON `sections`.`id` = `enrollments`.`section_id` WHERE `enrollments`.`student_id` = '1'
            Course Load (9.5ms)  SELECT `courses`.* FROM `courses` WHERE `courses`.`id` IN (1, 2, 3)
          => [#<Section:0x007fc40faa13a8
            id: 1,
            name: "1st period",
            course_id: "1",
           #<Section:0x007fc40faa11c8
            id: 5,
            name: "2nd period",
            course_id: "2",
           #<Section:0x007fc40faa0fe8
            id: 9,
            name: "3rd period",
            course_id: "3",
      

      那么现在:

      pry(main)> sections.first.course
      => #<Course:0x007fc40f6cc570
       id: 1,
       name: "English",
      

      没有额外的查询。为了简化事情,我会在注册表中添加一个 course_id 字段,然后添加:

      rails g student name
      class Student < ApplicationRecord
        has_many :enrollments
        has_many :sections, through: :enrollments
        has_many :courses, through: :enrollments
      end
      

      总之,关于join是怎么做的?让 ActiveRecord 为您完成工作,这就是它的用途。您提供的那些关系定义就是您指定所需连接的方式。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多