为什么我无法从关联的 table 获取数据?

Why can I not get data from an associated table?

在我的 Ruby on Rails 应用程序中,我试图显示进行预订的用户的 first_name 和 last_name。

在 bookings/index.html.erb 我有这个:

<% @bookings.each do |booking| %>
                                    <tr>
                                        <td><%= booking.user.first_name %> <%#= booking.user.last_name %></td>
                                        <td><%= booking.showing.film.title %></td>
                                        <td><%= booking.showing.show_date %></td>
                                        <td><%= booking.showing.show_time.strftime("%H:%M") %></td>
                                        <td></td>
                                        <td><%= booking.seat.row_letter %><%= booking.seat.number %></td>
                                        <td style="padding-right:65px">
                                            <%#= link_to 'Edit', edit_user_path(user) %>  
                                            <%#= link_to 'Show', user_path(user) %>  
                                            <%#= link_to 'Destroy', user_path(user), method: :delete, data: { confirm: 'Are you sure?' } %>
                                        </td>
                                    </tr>
                                <% end %>

booking.showing.film.title 和下面的代码有效,但使用 booking.user.first_name 我得到错误:

NoMethodError in Bookings#index     
undefined method `first_name' for nil:NilClass

但我认为我的模型设置正确,user.rb:

has_many :bookings

bookings.rb:

belongs_to :user

bookings_controller:

class BookingsController < ApplicationController
  before_action :set_booking, only: [:show, :edit, :update, :destroy]

  # GET /bookings
  # GET /bookings.json
  def index
    @bookings = Booking.all
  end

  # GET /bookings/1
  # GET /bookings/1.json
  def show
  end

  # GET /bookings/new
  def new
    @booking = Booking.new
  end

  # GET /bookings/1/edit
  def edit
  end

  # POST /bookings
  # POST /bookings.json
  def create
    @booking = Booking.new(booking_params)

    respond_to do |format|
      if @booking.save
        format.html { redirect_to @booking, notice: 'Booking was successfully created.' }
        format.json { render :show, status: :created, location: @booking }
      else
        format.html { render :new }
        format.json { render json: @booking.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /bookings/1
  # PATCH/PUT /bookings/1.json
  def update
     respond_to do |format|
      if @booking.update(booking_params)
        format.html { redirect_to @booking, notice: 'Booking was successfully updated.' }
        format.json { render :show, status: :ok, location: @booking }
      else
        format.html { render :edit }
        format.json { render json: @booking.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /bookings/1
  # DELETE /bookings/1.json
  def destroy
     @booking.destroy
    respond_to do |format|
      format.html { redirect_to bookings_url, notice: 'Booking was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_booking
      @booking = Booking.find(params[:id])
    end

   # Never trust parameters from the scary internet, only allow the white list through.
    def booking_params
      params.require(:booking).permit(:showing_id, :user_id, :seat_id)
    end
end

我错过了什么吗?属性名称和 table 名称正确。

因为 user 是 nil 所以 booking.user 抛出错误

试试这个booking.try(:user).try(:first_name)

 <% @bookings.each do |booking| %>
                                    <tr>
                                        <td><%= booking.try(:user).try(:first_name) %> <%= booking.try(:user).try(:last_name) %></td>
                                        <td><%= booking.showing.film.title %></td>
                                        <td><%= booking.showing.show_date %></td>
                                        <td><%= booking.showing.show_time.strftime("%H:%M") %></td>
                                        <td></td>
                                        <td><%= booking.seat.row_letter %><%= booking.seat.number %></td>
                                        <td style="padding-right:65px">
                                            <%#= link_to 'Edit', edit_user_path(user) %>  
                                            <%#= link_to 'Show', user_path(user) %>  
                                            <%#= link_to 'Destroy', user_path(user), method: :delete, data: { confirm: 'Are you sure?' } %>
                                        </td>
                                    </tr>
                                <% end %>

有关点击的零处理的更多信息 try

您已正确设置关联。 但问题是,您在创建 booking 记录时是否添加了 user_id

<% @bookings.each do |booking| %>
  <tr>
     <td><%= booking.user.try(:first_name) %> <%#= booking.user.try(:last_name) %></td>
     <td><%= booking.showing.film.title %></td>
     <td><%= booking.showing.show_date %></td>
     <td><%= booking.showing.show_time.strftime("%H:%M") %></td>
     <td></td>
     <td><%= booking.seat.row_letter %><%= booking.seat.number %></td>
     <td style="padding-right:65px">
       <%#= link_to 'Edit', edit_user_path(user) %>  
       <%#= link_to 'Show', user_path(user) %>  
       <%#= link_to 'Destroy', user_path(user), method: :delete, data: { confirm: 'Are you sure?' } %>
     </td>
  </tr>
<% end %>

使用方法try将确保不会发生错误

booking.user.try(:first_name) # display user first name if exist
booking.user.try(:last_name)  # display user last  name if exist

打开 rails c 并检查您的 booking 记录中没有 user_id

使用多个点违反了 Law of Demeter 并被视为 不良 做法。要遵循得墨忒耳法则,可以将delegate名称改为user模型并在user创建实例方法class改为return全名,

预订class

class Booking < ActiveRecord::Base
  belongs_to :user
  delegate :full_name, to: :user, prefix: true, allow_nil: true
end

用户class

class User < ActiveRecord::Base
  has_many :bookings

  def full_name
    "#{first_name} #{last_name}"
  end
end

风景

<td><%= booking.user_full_name %></td>

如果用户 nil 此函数将 return 优雅地 return nil 而不会抱怨没有用户。

委托方法调用总是更好(不要指 delegate 作为关键字)而不是调用这么多点,以 booking.showing.film.title 为例,有很多东西这可能会出错,如果预订或放映或电影更改了任何属性,您需要不断迭代所有视图并每次修复每一个视图,而不是尝试创建一个集中函数,例如 booking.film_title ,那么你只需要在一个地方维护它。