在 Rails 中连接两个具有多对多关系的模型

问题描述 投票:0回答:1

我有一个 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

有没有更有效的方法使用活动记录对象关系映射来做到这一点?

ruby-on-rails ruby activerecord many-to-many object-relational-model
1个回答
0
投票

在您的示例中,迭代

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
© www.soinside.com 2019 - 2024. All rights reserved.