在 Rails ( 4.1.5 / ruby 2.0.0p481 / win64 ) 应用程序中,我在Student和Course之间有一个多对多的关系,以及一个代表关联的连接模型StudentCourse,并且有一个名为start的附加属性(默认设置关于“假”)。
我还在由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将返回如下表:
学生.all
+----+-----------+
| id | name |
+----+-----------+
| 1 | Aidan |
| 2 | Alison |
| 3 | Elizabeth |
+----+-----------+
课程.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}})
其中还包括这些课程的其他学生。
但是假设我想渲染一个学生尚未开始的课程,以及该课程的所有其他学生(已经开始或未开始课程) [请阅读下面的更新部分,我正在寻找恰恰相反!]
我认为最简单的方法是查询连接表,例如:
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 } )
这对于学生来说,在连接表“开始”列中具有“真实”值的所有课程。(同样,在连接表中,每个学生课程记录都是“复合”唯一的,因此给定的 student_id 和 course_id 只能有一个唯一的记录)。
通过下一个查询,对于“aiden_started_courses”之一,我可以取消所有在“started”上具有错误值的相关学生课程关联
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数组中的一门课程获得了这个,但是我如何能够构建一个查询来返回所有Aiden 开始课程的数据?
是否有可能在一行中做到这一点?我知道我可能可以使用 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?但我怎么能这样做呢?
谢谢你的建议。我为冗长道歉..但希望它会有所帮助..