失礼します
ActiveRecordで、一対多の関係にあるレコード同士のincludesで悩んでいるものです。
ここでは説明として、多対多の関係にあるモデル、User
とLesson
クラスを作成します
class User has_many :user_lessons, dependent: :destroy has_many :lessons, through: user_lessons end class UserLesson belongs_to :user belongs_to :lesson end class Lesson has_many :user_lessons, dependent: :destroy has_many :users, through: user_lessons end
ここで各々のUserが持っているLessonレコードの一覧を表示するような以下のテーブルを作ります。
<table> <thead> <tr> <th>ユーザー名</th> <th>レッスン</th> </tr> </thead> <tbody> <% @users.each do |user| %> <tr> <td><%= user.name %></td> <td> <ul> <% user.user_lessons.each do |u| %> <li><%= u.lesson.name %></li> <% end %> </ul> </td> </tr> <% end %> </tbody> </table>
このページに渡す@userを
@user = User.includes(:user_lessons) .joins(:user_lessons) .merge(UserLesson.where(lesson_id: 1144))
このように設定すると、
SELECT COUNT(*) FROM "users" INNER JOIN "user_lessons" ON "user_lessons"."user_id" = "users"."id" WHERE "user_lessons"."lesson_id" = ? [["lesson_id", 1144]] SELECT DISTINCT "users"."id" FROM "users" INNER JOIN "user_lessons" ON "user_lessons"."user_id" = "users"."id" WHERE "user_lessons"."lesson_id" = ? LIMIT ? OFFSET ? [["lesson_id", 1144], ["LIMIT", 20], ["OFFSET", 0]] SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, "users"."created_at" AS t0_r2, "users"."updated_at" AS t0_r3, "user_lessons"."id" AS t1_r0, "user_lessons"."user_id" AS t1_r1, "user_lessons"."lesson_id" AS t1_r2, "user_lessons"."created_at" AS t1_r3, "user_lessons"."updated_at" AS t1_r4 FROM "users" INNER JOIN "user_lessons" ON "user_lessons"."user_id" = "users"."id" WHERE "user_lessons"."lesson_id" = ? AND "users"."id" IN (28, 56, 34, 6, 2) [["lesson_id", 1144]] SELECT "lessons".* FROM "lessons" WHERE "lessons"."id" = ? LIMIT ? [["id", 1144], ["LIMIT", 1]] CACHE (0.0ms) SELECT "lessons".* FROM "lessons" WHERE "lessons"."id" = ? LIMIT ? [["id", 1144], ["LIMIT", 1]] CACHE (0.0ms) SELECT "lessons".* FROM "lessons" WHERE "lessons"."id" = ? LIMIT ? [["id", 1144], ["LIMIT", 1]] CACHE (0.0ms) SELECT "lessons".* FROM "lessons" WHERE "lessons"."id" = ? LIMIT ? [["id", 1144], ["LIMIT", 1]] CACHE (0.0ms) SELECT "lessons".* FROM "lessons" WHERE "lessons"."id" = ? LIMIT ? [["id", 1144], ["LIMIT", 1]]
このようなSQLが出て、それぞれのUserデータのレッスン欄に、idが1144のレッスンだけが表示されて、望み通りの表示ではなくなってしまいます。
一方でこのようにincludesの段階でlessonをuser_lessonsと関連づけておくと
@user = User.includes({ user_lessons: lesson }) .joins(:user_lessons) .merge(UserLesson.where(lesson_id: 1144))
SELECT COUNT(*) FROM "users" INNER JOIN "user_lessons" ON "user_lessons"."user_id" = "users"."id" WHERE "user_lessons"."lesson_id" = ? [["lesson_id", 1144]] SELECT "users".* FROM "users" INNER JOIN "user_lessons" ON "user_lessons"."user_id" = "users"."id" WHERE "user_lessons"."lesson_id" = ? LIMIT ? OFFSET ? [["lesson_id", 1144], ["LIMIT", 20], ["OFFSET", 0]] SELECT "user_lessons".* FROM "user_lessons" WHERE "user_lessons"."user_id" IN (28, 56, 34, 6, 2) SELECT "lessons".* FROM "lessons" WHERE "lessons"."id" IN (1034, 1061, 1062, 1144, 1029, 1142, 1148, 1145, 1139, 1111, 1040, 1088, 1112, 1056, 1078, 1113, 1075)
それぞれのUserデータが持つすべてのレッスン名がレッスン欄に表示されて、理想通りになります。
SQL的には前者は子要素で、後者は親要素で絞り込んでいるのだろうということは想像できるのですが、なぜそのようになるのかが不明です。
どなたかご教授ください。宜しくお願い致します。
あなたの回答
tips
プレビュー