我有一个ruby on rails API,它在以下模型之间有多对多的关系:
class Course < ApplicationRecord
has_many :student_courses
has_many :students, through: :student_courses
end
class Student < ApplicationRecord
has_many :student_courses
has_many :courses, through: :student_courses
end
class StudentCourse < 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
有没有一种更有效的方法可以使用活动记录对象关系Map来实现这一点?
2条答案
按热度按时间jxct1oxe1#
在您的示例中,迭代
Course.all.each
,然后在每次迭代中调用course.students
将导致N+1
问题。这意味着将有一个数据库查询来获取所有课程,以及N个额外的数据库查询来加载列表中每个课程的学生。为了避免N+1个查询,Ruby on Rails允许使用
includes
在一个或最多两个查询中快速加载学生和课程另一个优化可以通过使用
Enumerable#map
重用现有数组来减少内存消耗,而不是使用each
迭代数组并将转换后的数据复制到新数组中。把它放在一起:
li9yvcax2#
你不需要重新发明JSON序列化轮子。ActiveModel已覆盖。
如果你需要更高级的序列化或者想避免控制器膨胀,有一些gem,比如ActiveModelSerializers,jBuilder和whole plethora of gems that do jsonapi.org style serialization。
请注意,生成的JSON略有不同:
但这实际上是一件好事,因为你可能想在响应中包含学生的其他属性,这将使你在不破坏现有前端代码的情况下做到这一点。