我的 "unpermitted parameter: :passenger" Rails 错误来自哪里?

Where is my "unpermitted parameter: :passenger" Rails error coming from?

我正在构建一个带有 Rails 的航班预订应用程序,可以让您 select 机场、日期和乘客数量。一旦你 select 机场和日期,它会给你单选按钮 select 你想要的航班,一旦你点击提交,你就会被带到一个预订确认页面,要求你提供乘客信息.

确认页面 (bookings#new) 有一个嵌套表单,用于将乘客包含在预订对象中。为此,我首先设置了以下模型和关联:

class Passenger < ApplicationRecord

    belongs_to :booking

end
class Booking < ApplicationRecord

    belongs_to :flight
    has_many :passengers 

    accepts_nested_attributes_for :passengers 

    
end

以及导致以下架构表的相关迁移:


  create_table "bookings", force: :cascade do |t|
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.integer "flight_id"
    t.integer "passenger_id"
  end
  create_table "passengers", force: :cascade do |t|
    t.string "name"
    t.string "email"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.integer "booking_id"
    t.index ["booking_id"], name: "index_passengers_on_booking_id"
  end

据我了解,流程是这样的: 用户 select 的航班 -> 用户提交航班,通过我控制器上的 #new 方法转到 Booking#new:

class BookingsController < ApplicationController

    def new
        @booking = Booking.new
        @flight = Flight.find(params[:flight_id]) 
        params[:passengers_number].to_i.times do #params passed from select flight page
            @booking.passengers.build
            end
    end

然后,我构建的表单将接管 new.html.erb:

 <%= form_with model: @booking, url: bookings_path do |f| %>
            <%= f.hidden_field :flight_id, value: @flight.id %>

            <% @booking.passengers.each_with_index do |passenger, index| %>
                <%= f.fields_for passenger, index: index do |form| %>

                <h4><%= "Passenger #{index+1}"%> <br> </h4>

                    <%= form.label :name, "Full name:" %>
                    <%= form.text_field :name %>

                    <%= form.label :email, "Email:" %>
                    <%= form.email_field :email %>

             <% end %>
        <% end %>

            <%= f.submit "Confirm details"%>
        <% end %>

我填写姓名和电子邮件,单击 'Confirm details',我在终端上收到此错误:

Unpermitted parameter: :passenger

为什么?我在我的 Booking 模型上设置了 accepts_nested_attributes_for :passengers,我的 booking_params 方法是:

 def booking_params
        params.require(:booking).permit(:flight_id,
            :passengers_attributes => [:name, :email, :passenger_id, :created_at, :updated_at])
      end

我的#create 方法是:

 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 { redirect_to root_path,  alert: "booking failed, #{@booking.errors.full_messages.first}" , status: :unprocessable_entity }
                format.json { render json: @booking.errors, status: :unprocessable_entity }
            end
          end
    end

有什么我没有适当允许的吗?请注意,如果我将 booking_params 设置为 params.require(:booking).permit!,它会给我一个 unknown attribute 'passenger' for Booking 错误。但至少据我所知,我已经定义了关于乘客和预订的关联和数据库。

提前致谢

编辑:产生错误的服务器日志是:

Started POST "/bookings" for ::1 at 2021-08-27 17:52:17 +0300
Processing by BookingsController#create as HTML
  Parameters: {"authenticity_token"=>"[FILTERED]", "booking"=>{"flight_id"=>"5", "passenger"=>{"0"=>{"name"=>"Jason Smason", "email"=>"jason@ymail.com"}, "1"=>{"name"=>"Joe Smith", "email"=>"Joe@smith.com"}}}, "commit"=>"Confirm details"}
Unpermitted parameter: :passenger

Edit2:我听从了 PCurell 的建议,将我的 fields_for 更改为 <%= f.fields_for :passengers, passenger, index: index do |form| %>,并将我的预订参数更改为 :passenger => [:name, :email, :passenger_id, :created_at, :updated_at])

产生了不同的错误:Unpermitted parameter: passengers_attributes'. So I changed my controller's booking_params` 到:

def booking_params 
        params.require(:booking).permit(:flight_id, 
            :passengers_attributes => [:name, :email, :passenger_id, :created_at, :updated_at],
            :passenger => [:name, :email, :passenger_id, :created_at, :updated_at])
end

有了这个,我成功地创建了一个有 2 名乘客的预订。然而,乘客一片空白;他们的 nameemail 为零。日志说:

Parameters: {"authenticity_token"=>"[FILTERED]", "booking"=>{"flight_id"=>"1", "passengers_attributes"=>{"0"=>{"0"=>{"name"=>"John Smith", "email"=>"John@smith.com"}}, "1"=>{"1"=>{"name"=>"Burger King", "email"=>"bk@bk.com"}}}}, "commit"=>"Confirm details"}
Unpermitted parameter: :0
Unpermitted parameter: :1

我可能能够硬编码 :0 和 :1 以通过,但肯定不是 Rails 方式。有没有办法动态地让他们进来?还是我整件事都做错了?

正如@Rockwell Rice 在他的评论中提到的那样:

这里的问题是您允许了错误的参数。

      def booking_params
        params.require(:booking).permit(:flight_id,
            :passenger => [:name, :email, :passenger_id, :created_at, :updated_at])
      end

应该可以。

尽管您可能会遇到另一个错误。

我认为您 fields_for 构建错误。

这应该是您要查找的内容(您不必更改 booking_params)

<%= form_with model: @booking, url: bookings_path do |f| %>
            <%= f.hidden_field :flight_id, value: @flight.id %>

            <% @booking.passengers.each_with_index do |passenger, index| %>
                <%= f.fields_for :passengers, passenger, index: index do |form| %>

                <h4><%= "Passenger #{index+1}"%> <br> </h4>

                    <%= form.label :name, "Full name:" %>
                    <%= form.text_field :name %>

                    <%= form.label :email, "Email:" %>
                    <%= form.email_field :email %>

             <% end %>
        <% end %>

            <%= f.submit "Confirm details"%>
        <% end %>

以上应该有效。

如果您不太关心索引,这也行得通:

         <%= form_with model: @booking, url: bookings_path do |f| %>
            <%= f.hidden_field :flight_id, value: @flight.id %>
            <%= f.fields_for @booking.passengers do |form| %>

                <%= form.label :name, "Full name:" %>
                <%= form.text_field :name %>

                <%= form.label :email, "Email:" %>
                <%= form.email_field :email %>

            <% end %>

            <%= f.submit "Confirm details"%>
        <% end %>

您对 fields_for 的用法不正确。当您在指南中查看 the example 时,您会注意到如果用于集合,则无需用 .each 将其包装。

10.2 Nested Forms

The following form allows a user to create a Person and its associated addresses.

<%= form_with model: @person do |form| %>
  Addresses:
  <ul>
    <%= form.fields_for :addresses do |addresses_form| %>
      <li>
        <%= addresses_form.label :kind %>
        <%= addresses_form.text_field :kind %>

        <%= addresses_form.label :street %>
        <%= addresses_form.text_field :street %>
        ...
      </li>
    <% end %>
  </ul>
<% end %>

When an association accepts nested attributes fields_for renders its block once for every element of the association. In particular, if a person has no addresses it renders nothing. A common pattern is for the controller to build one or more empty children so that at least one set of fields is shown to the user. The example below would result in 2 sets of address fields being rendered on the new person form.

def new
  @person = Person.new
  2.times { @person.addresses.build }
end

将此应用于您的代码时,删除 .each 包装并将 passenger 更改为 :passengers 应该可以解决问题。您可以通过传递给 fields_for 块的 FormBuilder 实例 (form) 访问 index

<%= form_with model: @booking, url: bookings_path do |f| %>
  <%= f.hidden_field :flight_id, value: @flight.id %>

  <%= f.fields_for :passengers do |form| %>
    <h4>Passenger <%= form.index + 1 %></h4>

    <%= form.label :name, "Full name:" %>
    <%= form.text_field :name %>

    <%= form.label :email, "Email:" %>
    <%= form.email_field :email %>
  <% end %>

  <%= f.submit "Confirm details"%>
<% end %>