呈现连接模型及其关联模型的JSON对象

时间:2014-11-07 07:48:27

标签: ruby-on-rails ruby json activerecord custom-object

在Rails(4.1.5 / ruby​​ 2.0.0p481 / win64)应用程序中,我在学生课程之间存在多对多关系和连接模型 StudentCourse 代表关联,并且另一个属性,名为已启动(默认设置为&#34; false&#34;)。< / p>

我还在 student_id course_id 组成的连接表中添加了一个索引,并设置了一个唯一的检查,就像这样

t.index [:student_id, :course_id], :unique => true, :name => 'by_student_and_course'

我希望它是一个复合主键,但由于在rails中没有复合主键(不使用gem),我还添加了一个名为id的主键:

t.column :id, :primary_key

现在我看到关联是通过以下方式创建的:

Student.first.courses.create(:name => "english")

Course.first.students << Student.first

这很好,我认为这是预期的行为。

<小时/> 也就是说,我正在努力围绕ActiveRecord查询中的关联解决方案。让我更好地解释一下:

作为参考, Student.all Course.all StudentCourses.all 会返回以下表格:

Student.all

+----+-----------+
| id | name      |
+----+-----------+
| 1  | Aidan     |
| 2  | Alison    |
| 3  | Elizabeth |
+----+-----------+

Course.all

+----+----------+------------------+
| id | name     | description      |
+----+----------+------------------+
| 1  | english  | desc. here       |
| 2  | music    | desc. here       |
| 3  | dance    | desc. here       |
| 4  | science  | desc. here       |
| 5  | french   | desc. here       |
| 6  | arts     | desc. here       |
+----+----------+------------------+

StudentCourse.all

+-------------+------------+------------+----+
| course_id   | student_id | started    | id |
+-------------+------------+------------+----+
| 1           | 1          | false      | 1  |
| 2           | 1          | false      | 2  |
| 3           | 1          | false      | 3  |
| 1           | 2          | true       | 4  |
| 2           | 2          | true       | 5  |
| 4           | 3          | false      | 6  |
| 5           | 2          | false      | 7  |
| 5           | 1          | true       | 8  |
| 6           | 2          | false      | 9  |
+-------------+------------+------------+----+


到目前为止,我可以愉快地渲染所有课程的json对象,以及每个课程的所有学生的名字,如下所示:

render json: Course.all.to_json(:include => {:students => {:only => :name}})

我也可以轻松呈现学生参加或即将参加的所有课程

render json: @student.courses.to_json(:include => {:students => {:only => :name}})

还包括其他学生参加这些课程。

但是假设我想要提供一个学生尚未开始的学生课程,以及所有其他在该课程上学习的课程(有或没有开课) [请阅读下面的更新部分,我实际上正在寻找相反的事情!]

我认为easyieast方法是在join-table中查询类似的内容:

StudentCourse.all.where(student_id: @student.id, started: false)

+-------------+------------+------------+----+
| course_id   | student_id | started    | id |
+-------------+------------+------------+----+
| 5           | 2          | false      | 7  |
| 6           | 2          | false      | 9  |
+-------------+------------+------------+----+

但是我如何从这个结果表(关联对象)继续获取包含课程名称(和所有其他属性)以及包括学生的精美包装的json对象,就像我做的那样:Course.all.to_json(:include => {:students => {:only => :name}})

我认为我在这里缺少一些关键概念的基本知识,但是在这一点上我甚至无法识别它们并且非常感谢一些帮助...谢谢。


<小时/>

更新

我刚刚意识到以下部分是我最初想要做的事情。这是相反的事情。在所有这些细节中,我迷失在路上。如果我在这里添加它,我希望它没问题。

所以,考虑到一个学生(让他们称他为Aiden),我需要返回一个JSON对象,该对象只包含他所在的课程以及他已经开始当此类课程中有其他学生尚未启动时,其中还必须包含每个课程的学生姓名。

因此...

我现在有:

aiden_started_courses = Student(1).courses.where(:student_courses => {:started => true } )

对于学生,所有课程都有&#34; true&#34;连接表中的值&#34;已开始&#34;柱。 (再次在连接表中,每个 student-course 记录都是&#34;复合&#34;唯一,因此给定student_id和course_id只能有一个唯一记录。

使用下一个查询,一个的&#34; aiden_started_courses&#34;我可以启动所有相关的学生课程关联,这些关联在&#34;开始&#34;

上有错误的价值
aiden_started_courses[0].student_courses.where(:started => false).includes(:student).to_json(:include => :student)

+-------------+------------+------------+----+
| course_id   | student_id | started    | id |
+-------------+------------+------------+----+
| 1           | 2          | false      | 4  |
+-------------+------------+------------+----+
| 1           | 9          | false      | 5  |
+-------------+------------+------------+----+

所以这就是问题所在:我已经设法在 aiden_started_courses 数组中为单个课程设置了这个,但是我如何能够构建一个返回此数据的查询< em>所有的艾登的课程开始?

有可能在一行中完成吗?我知道我可能会使用Ruby枚举器循环,但我有点觉得我会在Rails编码约定级别和性能级别上打破一些模式? (再次遇到N + 1问题?)......


到目前为止我能做什么:

我想到了这一点,我找到了所有未开始给定用户已经开始的课程的学生:

Student.includes(:student_courses).
where(:student_courses => { :started => false, :course_id => aiden.courses.where
           (:student_courses => {started: true}).ids  } ) 

或者这个:

Course.includes(:students).where(:student_courses => {:started => false, 
   :course_id => aiden.courses.where(:student_courses => {:started =>true}).ids })

查找给定学生已开始的所有课程,如果这些课程包括尚未启动它们的学生

但我真正需要的是获得这样的JSON对象:

[
    {
        "id": 1,
        "name": "english",
        "students": [
            {"name": "ALison"},
            {"name": "Robert"}]
    },
    {
        "id": 2,
        "name": "music",
        "description": null,
        "students": [
            {"name": "Robert"},
            {"name": "Kate"}]
    }
]


在那里我可以看到特定学生所开设的课程,但只有那些尚未开始学习的学生,以及那些学生的名字......

我认为可能没有办法通过常规AR查询获得这个,所以也许应该手动构建JSON?但我怎么能这样做?


谢谢你。我为冗长而道歉..但希望它会有所帮助......

2 个答案:

答案 0 :(得分:8)

使用scope为您带来好处:

class Course < ActiveRecord::Base
  # ...
  scope :not_started, -> { joins(:student_courses) \
                                      .where(student_courses: {started: false}) }

  scope :with_classmates, -> { includes(:students) } # use eager loading
end

然后致电:

@student.courses.not_started.with_classmates \
                                     .to_json(include: {students: {only: :name}})

输出:

[
    {
        "id": 1,
        "name": "english",
        "description": null,
        "students": [
            {"name": "Aiden"},
            {"name": "Alison"}]},
    {
        "id": 2,
        "name": "music",
        "description": null,
        "students": [
            {"name": "Aiden"},
            {"name": "Alison"}]},
    {
        "id": 3,
        "name": "dance",
        "description": null,
        "students": [
            {"name": "Aiden"}]}]

答案 1 :(得分:5)

使用JBuilder,它默认带有Rails。忽略以'#'开头的行:

Jbuilder.new do |j|

    # courses: [{
    j.courses <student.courses - replace this with whatever variable> do |course|

      # id: <course.id>
      j.id course.id 

      # name: <course.name>
      j.name course.name

      # students: [{
      j.students <course.students - replace with whatever variable> do |student|

          # name: <student.name>
          j.name student.name

      end
      # }]

   end
   # }]

end

不是很多代码。删除注释并简化某些功能,它将如下所示:

student_courses = <...blah...>
json = Jbuilder.new do |j|
  j.courses student_courses do |course|
    j.(course, :id, :name)
    j.students <course.students - whatever method here>, :name
  end
end.target!

查看他们的guide,它非常棒,可以在纯Ruby DSL中生成JSON。因此,请继续使用您想要获取学生/课程/学生课程的任何红宝石代码。