В приложении Rails ( 4.1.5 / ruby 2.0.0p481 / win64 ) у меня есть отношение «многие ко многим» между Студентом и Курсом и моделью соединения StudentCourse, который представляет ассоциацию и имеет дополнительный атрибут, называемый started (по умолчанию установлено значение "false").
Я также добавил индекс в таблицу соединений, состоящую из student_id и course_id, и установил для него уникальную проверку, например
t.index [:student_id, :course_id], :unique => true, :name => 'by_student_and_course'
Я хотел, чтобы это был составной первичный ключ, но поскольку в рельсах нет составных первичных ключей (без использования драгоценного камня), я также добавил первичный ключ с именем id:
t.column :id, :primary_key
Теперь я вижу, что ассоциации создаются либо путем выполнения:
Student.first.courses.create(:name => "english")
or
Course.first.students << Student.first
Это нормально, и это ожидаемое поведение, я полагаю.
Тем не менее, я изо всех сил пытаюсь осмыслить разрешения ассоциаций в запросах ActiveRecord. Позвольте мне объяснить это лучше:
Для справки, Student.all, Course.all и StudentCourses.all будут возвращать такие таблицы:
Студент.все
+----+-----------+
| id | name |
+----+-----------+
| 1 | Aidan |
| 2 | Alison |
| 3 | Elizabeth |
+----+-----------+
Курс.все
+----+----------+------------------+
| 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}})
?
Я думаю, что мне не хватает некоторых базовых знаний о некоторых важных ключевых понятиях, но на данный момент я даже не могу их идентифицировать и был бы очень признателен за помощь... спасибо.
Обновлять:
Я только что понял, что следующая часть — это то, что я изначально пытался сделать. Это противоположное. Среди всех этих подробностей я заблудился по пути. Я надеюсь, что это нормально, если я просто добавлю это здесь.
Итак, для данного студента (назовем его Эйден) мне нужно вернуть объект JSON, содержащий только те курсы, которые он посещает и которые начал, только когда в таких курсах есть другие студенты, которые не начинали их, и он также должен включать имена этих студентов для каждого курса.
So...
Теперь у меня есть:
aiden_started_courses = Student(1).courses.where(:student_courses => {:started => true } )
который для студента принимает все курсы, которые имеют значение "true" в столбце "начало" таблицы соединения. (опять же, в таблице соединения каждая запись student-course является "составно" уникальной, поэтому может быть только одна уникальная запись для данных student_id и course_id).
С помощью следующего запроса для одного из "aiden_started_courses" я могу получить все относительные ассоциации студент-курсы, которые имеют ложное значение для "начало"
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, но мне кажется, что я нарушил бы какой-то шаблон как на уровне соглашения о кодировании 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 вручную? Но как я мог это сделать?
Спасибо в adv. и прошу прощения за многословие.. но, надеюсь, это поможет..
.to_json
в конец:StudentCourse.all.where(student_id: @student.id, started: false).to_json
. - person Aleksei Matiushkin   schedule 07.11.2014:belongs_to
и:has_many
для соответствующихActiveRecord
, как указано здесь: api.rubyonrails.org/classes/ActiveRecord/Associations/ - person Aleksei Matiushkin   schedule 07.11.2014has_many ... :through ...
s иbelongs_to
s в моделях. Пожалуйста, не могли бы вы просто прочитать вопросы с большим вниманием, если вам действительно нужно это прокомментировать? Спасибо. - person jj_   schedule 07.11.2014