9

Rails ( 4.1.5 / ruby​​ 2.0.0p481 / win64 ) アプリケーションでは、 StudentCourseの間に多対多の関係があり、関連付けを表す結合モデルStudentCourseがあり、 started (デフォルトで設定)という追加の属性があります「偽」で)。

また、 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.allCourse.allStudentCourses.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 に対して一意のレコードが 1 つだけ存在する可能性があります)。

次のクエリでは、「aiden_started_courses」の1 つに対して、「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 が開始したすべてのコースについてこのデータを返すクエリを作成するにはどうすればよいでしょうか?

1行でそれを行うことは可能ですか?おそらく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 を手動で作成する必要がありますか? しかし、どうすればそれができますか?


前もって感謝します。冗長で申し訳ありません..しかし、うまくいけばそれは..

4

2 に答える 2

9

あなたの利益のために使用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"}]}]
于 2014-11-07T11:19:18.823 に答える
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!

彼らのガイドをチェックしてください。素の Ruby DSL で JSON を生成するのはとても素晴らしいことです。ですから、学生/コース/学生コースをフェッチしたいルビーコードを使用してください。

于 2014-11-11T15:46:28.883 に答える