Визуализация объекта JSON модели соединения и связанных с ней моделей
в рельсах (4.1.5 / ruby 2.0.0p481 / win64 ) приложение у меня есть отношения "многие ко многим" между студент и курс и модель StudentCourse, который представляет ассоциацию, и дополнительный атрибут под названием started (по умолчанию установлено значение "false").
я также добавил индекс в таблицу соединений из student_id и идентификатор_курса, и установите уникальную проверку на этом, как это
t.index [:student_id, :course_id], :unique => true, :name => 'by_student_and_course'
я хотел, чтобы это был составной первичный ключ, но поскольку в rails нет составных первичных ключей (без использования драгоценного камня), я также добавил первичный ключ под названием id:
t.column :id, :primary_key
теперь я вижу, что ассоциации создаются либо делать:
Student.first.courses.create(:name => "english")
или
Course.first.students << Student.first
это нормально, и это ожидаемое поведение, я полагаю.
Тем не менее, я изо всех сил пытаюсь обернуть свой ум вокруг резолюций ассоциации в запросах ActiveRecord. Позвольте мне объяснить это лучше:
Для справки:студент.все!--24-->, конечно.все!--24--> и StudentCourses.все!--24--> вернул бы такие таблицы:
студент.все!--27-->
+----+-----------+
| id | name |
+----+-----------+
| 1 | Aidan |
| 2 | Alison |
| 3 | Elizabeth |
+----+-----------+
конечно.все!--27-->
+----+----------+------------------+
| 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.все!--27-->
+-------------+------------+------------+----+
| 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}})
который также включает в себя других студентов для этих курсов.
но предположим, я хотел бы сделать курсы одного студента, которые студент еще не начал, вместе со всеми другими студентами, которые находятся на этом курсе (которые или не начали курс) [пожалуйста, прочитайте раздел Обновления ниже, я ищу противоположную вещь на самом деле!]
я думаю, что подход easieast заключается в запросе таблицы соединений для чего-то вроде:
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}})
?
я думаю, что мне не хватает некоторых базовых знаний о некоторых важных ключевых понятиях здесь, но на данный момент я даже не могу их идентифицировать и был бы очень признателен за некоторую помощь... спасибо.
обновление:
я просто понял, что следующая часть-это то, что я изначально пытался сделать. Все наоборот. Среди всех этих подробностей я заблудился на пути. Я надеюсь, что это нормально, если я просто добавлю его здесь.
Итак, учитывая студента (назовем его Эйденом), мне нужно вернуть объект JSON, содержащий только курсы, в которых он находится, и что он стал, только когда на таких курсах есть другие студенты, которые еще не начали их, и он также должен включать имена этих студентов для каждого курса.
так...
у меня теперь есть:
aiden_started_courses = Student(1).courses.where(:student_courses => {:started => true } )
который для a студент принимает курсы которые имеют значение " true "в столбце" started " таблицы соединений. (снова в таблице join each студент-курс запись" композитно " уникальна, поэтому для данного student_id и course_id может быть только одна уникальная запись).
со следующим запросом, для один из "aiden_started_courses" я могу снять все относительные студент-курсы ассоциации, имеющие значение false на "начало"
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 массив, но как бы я мог построить запрос, который возвращает эти данные для все Эйдена начал курсы?
можно ли сделать это в одну строку? Я знаю, что, вероятно, мог бы использовать циклы перечислителя Ruby, но я чувствую, что я бы нарушил какой-то шаблон как на рельсах уровень конвенции кодирования и уровень производительности? (снова нажимая 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 вручную? Но как смогу ли я это сделать?
Спасибо в adv. и прошу прощения за многословие.. но, надеюсь, это поможет..
2 ответов
используйте 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!
проверить их руководство, его довольно удивительно генерировать JSON в простой рубиновый DSL. Поэтому идите вперед и используйте любой код ruby, который вы хотите получить для студентов/курсов/studentcourses.
использовать 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"}]}]