我有一个 ruby on Rails api,它在以下模型之间具有多对多关系:
class Courses < ApplicationRecord
has_many :student_courses
has_many :students, through: :student_courses
end
class Students < ApplicationRecord
has_many :student_courses
has_many :courses, through: :student_courses
end
class StudentCourses < ApplicationRecord
belongs_to :student
belongs_to :courses
end
我想以以下格式提供 json:
[
{
"course": "English",
"students": [
"John",
"Sarah"
]
},
{
"course": "Maths",
"students": [
"John",
"Ella",
"Lee"
]
},
{
"course": "Classics",
"students": [
"Una",
"Tom"
]
}
]
目前我正在使用循环来执行此操作:
def index
@courses = Course.all
output = []
@courses.each do |course|
course_hash = {
course: course.name,
students: course.students.map { |student| student.name }
}
output << course_hash
end
render json: output.to_json
end
有没有更有效的方法使用活动记录对象关系映射来做到这一点?
在您的示例中,迭代
Course.all.each
,然后在每次迭代中调用 course.students
将导致 N+1
问题。这意味着将有一个数据库查询来获取所有课程,并有 N 个附加数据库查询来加载列表中每一门课程的学生。
为了避免 N+1 查询,Ruby on Rails 允许使用
includes
在一个或最多两个查询中预先加载学生和课程
Enumerable#map
重用已经存在的数组来减少内存消耗,而不是使用 each
迭代数组并将转换后的数据复制到新数组中。
放在一起:
def index
courses_with_students = Course.includes(:students).map do |course|
{ course: course.name, students: course.students.map(&:name) }
end
render json: courses_with_students.to_json
end