使用多个联接构建活动记录查询

时间:2017-10-21 02:27:16

标签: ruby-on-rails activerecord

我正在尝试了解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

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:0)

所以你问的是,考虑到学生参加了许多课程的许多部分,哪些课程是学生注册的?

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

或换句话说

学生1:M注册M:1节M:1课程

或学生M:M部分。注册是学生和科之间M:M关系的连接表,没有?

我们马上就看到学生应该:

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

注册是一个连接表,因此它需要学生和部分的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

总之,关于如何进行加入?让ActiveRecord为您完成工作,这就是它的用途。您提供的关系定义是指定所需联接的方式。