Open3

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

コードの解説

  1. Reservation.joins(schedule: :movie)

    • Reservation モデルを基準に、関連する schedulemovie を結合しています。
    • これにより、予約に対応する上映スケジュールと映画情報が結合されます。
  2. .where(movies: { is_showing: false })

    • movie テーブルの is_showing カラムが false の映画(上映していない映画)に関連する予約のみを絞り込みます。
  3. .includes(:schedule)

    • includes メソッドを使うことで、関連する schedule テーブルを効率よく取得します。これにより、ビューで @reservations を参照するときに N+1 問題が発生しにくくなります。

このコードで、上映していない映画に関連する予約一覧を取得できるようになります。

ポテトポテト

もちろん、もっと詳しく解説します。まず、エラーメッセージ undefined method 'movie' for an instance of Reservation の原因を理解し、どのように関連データを正しく取得するかを解説します。

モデル間の関係

まず、Reservation モデル、Schedule モデル、Movie モデルの間の関係をおさらいしましょう。

Reservation モデル

  • Reservation は座席の予約を管理します。
  • ReservationSchedule と関連しています(schedule_id を持っています)。
  • Reservation は直接的に Movie と関連していませんが、Schedule を通じて Movie と関連しています。

Schedule モデル

  • Schedule は映画の上映スケジュールを管理します。
  • ScheduleMovie と関連しており、movie_id を持っています。
  • ScheduleReservation と関連しており、schedule_id を持っています。

Movie モデル

  • Movie は映画の情報を管理します(name, description, is_showing など)。

したがって、Reservation モデルから Movie にアクセスするためには、ReservationScheduleMovie という順番で関連データにアクセスしなければなりません。


joinsincludes の使用

  • joins:SQL の INNER JOIN と同じで、指定した関連テーブルを結合してデータを取得します。これにより、条件に合致するデータのみが返されます。

  • includes:Eager Loading を使用して、関連するテーブルのデータをあらかじめ取得します。これにより、N+1 クエリ問題を防ぎ、パフォーマンスが向上します。

この2つを組み合わせることで、必要なデータを効率的に取得できます。


実際のコード

あなたが直面している問題は、Reservation から直接 movie にアクセスできないということです。なぜなら、Reservation には直接 movie という関連がないためです。しかし、ReservationSchedule を通じて 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 %>

解説:

  1. reservation.schedule

    • Reservation モデルは schedule_id を持っており、Schedule モデルのインスタンスに関連しています。
    • reservation.schedule で、予約されたスケジュール情報を取得できます。
  2. reservation.schedule.movie

    • Schedule モデルは movie_id を持っており、Movie モデルのインスタンスに関連しています。
    • reservation.schedule.movie で、スケジュールに関連付けられた映画の情報を取得できます。

このように、ReservationScheduleMovie と順番に関連データをたどっていく必要があります。


クエリの確認

次に、関連データを効率的に取得するためのクエリを見ていきます。

@reservations = Reservation.joins(schedule: :movie)
                           .where(movies: { is_showing: false })
                           .includes(schedule: :movie)

解説:

  1. joins(schedule: :movie)

    • joins を使うことで、ReservationSchedule、さらに ScheduleMovie を結合します。このように結合することで、movies.is_showing などの条件を使って映画の情報を絞り込むことができます。
    • ここで INNER JOIN として結合されるので、条件に一致するデータだけが取得されます。
  2. where(movies: { is_showing: false })

    • 映画が現在上映中でない(is_showingfalse)ものを条件に絞り込みます。
  3. includes(schedule: :movie)

    • includes を使って、ScheduleMovie のデータをあらかじめロードします。これにより、ビューで reservation.schedule.movie にアクセスしたときに、追加のクエリが発行されるのを防ぎ、パフォーマンスを向上させます。

最終的なコード

最終的に、ビューで reservation.schedule.movie.name のようにアクセスするために、次のように記述します。

@reservations = Reservation.joins(schedule: :movie)
                           .where(movies: { is_showing: false })
                           .includes(schedule: :movie)

これで、Reservation に関連する ScheduleMovie の情報を効率的に取得できます。

ビューでの使い方

<% @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 の各予約に関連する映画名や上映開始時間を正しく表示できるようになります。


まとめ

  • ReservationSchedule を通じて Movie と関連しています。
  • ビューでは、reservation.schedule.movie.name のように、関連データを順番にたどってアクセスする必要があります。
  • joinsincludes を使うことで、効率よく関連データを取得し、ビューで表示することができます。