rails 複数db
@reservations = Reservation.joins(schedule: :movie)
.where(movies: { is_showing: false }) # 上映していない映画
.includes(:schedule)
@reservations.include(:schedules)
の部分に問題があります。このコードでは、@reservations
に対して include
を呼び出していますが、Active Record では include
ではなく、includes
メソッドを使います。includes
メソッドを使うことで、N+1 問題を防ぎつつ関連するテーブルの情報を効率よく取得できます。
また、コードでは is_showing: false
の映画に関連する reservations
を取得しようとしていますが、@reservations
に取得したデータは「予約情報」ではなく、Movie
モデルのデータになっています。予約情報が欲しい場合、取得するデータをReservation
に変更する必要があります。
以下のコードで、「上映していない映画に関連する予約一覧」 を取得する方法を示します。
# app/controllers/admin/reservations_controller.rb
class Admin::ReservationsController < ApplicationController
def index
# 上映していない映画に関連する予約を取得
@reservations = Reservation.joins(schedule: :movie)
.where(movies: { is_showing: false }) # 上映していない映画
.includes(:schedule)
render 'index'
end
end
コードの解説
-
Reservation.joins(schedule: :movie)
-
Reservation
モデルを基準に、関連するschedule
とmovie
を結合しています。 - これにより、予約に対応する上映スケジュールと映画情報が結合されます。
-
-
.where(movies: { is_showing: false })
-
movie
テーブルのis_showing
カラムがfalse
の映画(上映していない映画)に関連する予約のみを絞り込みます。
-
-
.includes(:schedule)
-
includes
メソッドを使うことで、関連するschedule
テーブルを効率よく取得します。これにより、ビューで@reservations
を参照するときに N+1 問題が発生しにくくなります。
-
このコードで、上映していない映画に関連する予約一覧を取得できるようになります。
もちろん、もっと詳しく解説します。まず、エラーメッセージ undefined method 'movie' for an instance of Reservation
の原因を理解し、どのように関連データを正しく取得するかを解説します。
モデル間の関係
まず、Reservation
モデル、Schedule
モデル、Movie
モデルの間の関係をおさらいしましょう。
Reservation
モデル
-
Reservation
は座席の予約を管理します。 -
Reservation
はSchedule
と関連しています(schedule_id
を持っています)。 -
Reservation
は直接的にMovie
と関連していませんが、Schedule
を通じてMovie
と関連しています。
Schedule
モデル
-
Schedule
は映画の上映スケジュールを管理します。 -
Schedule
はMovie
と関連しており、movie_id
を持っています。 -
Schedule
はReservation
と関連しており、schedule_id
を持っています。
Movie
モデル
-
Movie
は映画の情報を管理します(name
,description
,is_showing
など)。
したがって、Reservation
モデルから Movie
にアクセスするためには、Reservation
→ Schedule
→ Movie
という順番で関連データにアクセスしなければなりません。
joins
と includes
の使用
-
joins
:SQL のINNER JOIN
と同じで、指定した関連テーブルを結合してデータを取得します。これにより、条件に合致するデータのみが返されます。 -
includes
:Eager Loading を使用して、関連するテーブルのデータをあらかじめ取得します。これにより、N+1
クエリ問題を防ぎ、パフォーマンスが向上します。
この2つを組み合わせることで、必要なデータを効率的に取得できます。
実際のコード
あなたが直面している問題は、Reservation
から直接 movie
にアクセスできないということです。なぜなら、Reservation
には直接 movie
という関連がないためです。しかし、Reservation
は Schedule
を通じて Movie
と関連しているので、次のようにアクセスします。
修正版ビュー
<% @reservations.each do |reservation| %>
<h2>映画名: <%= reservation.schedule.movie.name %></h2>
<p>上映開始時間: <%= reservation.schedule.start_time %></p>
<p>予約者: <%= reservation.name %></p>
<p>予約者のメール: <%= reservation.email %></p>
<% end %>
解説:
-
reservation.schedule
:-
Reservation
モデルはschedule_id
を持っており、Schedule
モデルのインスタンスに関連しています。 -
reservation.schedule
で、予約されたスケジュール情報を取得できます。
-
-
reservation.schedule.movie
:-
Schedule
モデルはmovie_id
を持っており、Movie
モデルのインスタンスに関連しています。 -
reservation.schedule.movie
で、スケジュールに関連付けられた映画の情報を取得できます。
-
このように、Reservation
→ Schedule
→ Movie
と順番に関連データをたどっていく必要があります。
クエリの確認
次に、関連データを効率的に取得するためのクエリを見ていきます。
@reservations = Reservation.joins(schedule: :movie)
.where(movies: { is_showing: false })
.includes(schedule: :movie)
解説:
-
joins(schedule: :movie)
:-
joins
を使うことで、Reservation
とSchedule
、さらにSchedule
とMovie
を結合します。このように結合することで、movies.is_showing
などの条件を使って映画の情報を絞り込むことができます。 - ここで
INNER JOIN
として結合されるので、条件に一致するデータだけが取得されます。
-
-
where(movies: { is_showing: false })
:- 映画が現在上映中でない(
is_showing
がfalse
)ものを条件に絞り込みます。
- 映画が現在上映中でない(
-
includes(schedule: :movie)
:-
includes
を使って、Schedule
とMovie
のデータをあらかじめロードします。これにより、ビューでreservation.schedule.movie
にアクセスしたときに、追加のクエリが発行されるのを防ぎ、パフォーマンスを向上させます。
-
最終的なコード
最終的に、ビューで reservation.schedule.movie.name
のようにアクセスするために、次のように記述します。
@reservations = Reservation.joins(schedule: :movie)
.where(movies: { is_showing: false })
.includes(schedule: :movie)
これで、Reservation
に関連する Schedule
と Movie
の情報を効率的に取得できます。
ビューでの使い方
<% @reservations.each do |reservation| %>
<h2>映画名: <%= reservation.schedule.movie.name %></h2>
<p>上映開始時間: <%= reservation.schedule.start_time %></p>
<p>予約者: <%= reservation.name %></p>
<p>予約者のメール: <%= reservation.email %></p>
<% end %>
これで、Reservation
の各予約に関連する映画名や上映開始時間を正しく表示できるようになります。
まとめ
-
Reservation
はSchedule
を通じてMovie
と関連しています。 - ビューでは、
reservation.schedule.movie.name
のように、関連データを順番にたどってアクセスする必要があります。 -
joins
とincludes
を使うことで、効率よく関連データを取得し、ビューで表示することができます。