I am trying to prepare an app where we can choose a flights and NUMBER OF PASSENGERS. Based on number of passengers(params[:_passengers] and chosen flight(params[:_chosen_flight_id]) I am preparing a view with form to gather Passengers data.
It's creating POST request to bookings#create action where I am trying to create 1 booking(flight_id,passenger_id) and associated passenger using booking.build_passenger method:
Started POST "/bookings" for 127.0.0.1 at 2024-12-18 17:18:29 +0100
Processing by BookingsController#create as TURBO_STREAM
Parameters: {"authenticity_token"=>"[FILTERED]", "_passengers"=>"2", "_chosen_flight_id"=>"7", "booking"=>{"0"=>{"passenger_attributes"=>{"passport_no"=>"123123123", "name"=>"Marek", "surname"=>"Surname"}}, "1"=>{"passenger_attributes"=>{"passport_no"=>"123123121", "name"=>"Marek", "surname"=>"Example"}}}, "button"=>""}
Flight Load (0.2ms) SELECT "flights".* FROM "flights" WHERE "flights"."id" = $1 LIMIT $2 [["id", 7], ["LIMIT", 1]]
↳ app/controllers/bookings_controller.rb:14:in `create'
Unpermitted parameters: :0, :1. Context: { controller: BookingsController, action: create, request: #<ActionDispatch::Request:0x00007f49722664a8>, params: {"authenticity_token"=>"[FILTERED]", "_passengers"=>"2", "_chosen_flight_id"=>"7", "booking"=>{"0"=>{"passenger_attributes"=>{"passport_no"=>"123123123", "name"=>"Marek", "surname"=>"Surname"}}, "1"=>{"passenger_attributes"=>{"passport_no"=>"123123121", "name"=>"Marek", "surname"=>"Example"}}}, "button"=>"", "controller"=>"bookings", "action"=>"create"} }
Completed 500 Internal Server Error in 2ms (ActiveRecord: 0.2ms (1 query, 0 cached) | GC: 0.0ms)
ActiveModel::ForbiddenAttributesError (ActiveModel::ForbiddenAttributesError):
Code below:
#bookings_controller
class BookingsController < ApplicationController
def new
@bookings = []
params[:_passengers].to_i.times do
@bookings << Booking.new
end
@bookings.each do |booking|
booking.build_passenger
end
@flight = Flight.find(params[:_chosen_flight_id])
end
def create
@flight = Flight.find(params[:_chosen_flight_id])
params[:_passengers].to_i.times do
i = 0
@booking = Booking.new
@booking.flight = @flight
@booking.build_passenger(params[:booking]["#{i}"][:passenger_attributes])
i = i + 1
puts "Booking no#{i}:"
puts @booking.inspect
end
end
private
def booking_params
params.require(:booking).permit(:flight_id, passenger_attributes: [:passport_no, :name, :surname])
end
end
#bookings/new.html.erb form
<%= form_tag bookings_path do %>
<%= hidden_field_tag :_passengers, params[:_passengers] %>
<%= hidden_field_tag :_chosen_flight_id, params[:_chosen_flight_id] %>
<ul>
<% @bookings.each_with_index do |booking, i| %>
Passenger information:
<%= fields_for "booking[#{i}]", booking do |booking_form| %>
<%= booking_form.fields_for :passenger do |passenger_form| %>
<li>
<%= passenger_form.label :passport_no %>
<%= passenger_form.text_field :passport_no %>
<%= passenger_form.label :name %>
<%= passenger_form.text_field :name %>
<%= passenger_form.label :surname %>
<%= passenger_form.text_field :surname %>
</li>
<% end %>
<% end %>
<% end %>
<%= button_tag "SUBMIT" %>
</ul>
<% end %>
Any ideas how to iterate over my params and create booking-passenger pairs?
Quite simply, you need to use booking_params
and not access the params
object directly.
In the example below I changed passenger_attributes
to passengers_attributes
and the relationship to has_many passengers
, then I changed the form to fields_for booking
and fields_for :passengers, booking.passengers.build
, you also need to accept_nested_attributes_for
passengers.
class BookingsController < ApplicationController
def new
@booking = Booking.new
(params[:_passengers].to_i || 0).times { @booking.passengers.build }
# @flight = Flight.find(params[:_chosen_flight_id])
end
def create
# @flight = Flight.find(params[:_chosen_flight_id])
@booking = Booking.new(booking_params)
if @booking.save
redirect_to @booking
else
respond_with @booking
end
end
private
def booking_params
params.require(:booking).permit(:flight_id, passengers_attributes: [:passport_no, :name, :surname])
end
end
class Booking < ApplicationRecord
has_many :passengers
has_one :flight
accepts_nested_attributes_for :passengers
end
<%= hidden_field_tag :_passengers, params[:_passengers] %>
<%= hidden_field_tag :_chosen_flight_id, params[:_chosen_flight_id] %>
<ul>
<%= form_with model: @booking do |booking_form| %>
<%= booking_form.fields_for :passengers do |passenger_form| %>
Passenger information:
<li>
<%= passenger_form.label :passport_no %>
<%= passenger_form.text_field :passport_no %>
<%= passenger_form.label :name %>
<%= passenger_form.text_field :name %>
<%= passenger_form.label :surname %>
<%= passenger_form.text_field :surname %>
</li>
<% end %>
<%= booking_form.submit %>
<% end %>
</ul>
tarted POST "/bookings" for 127.0.0.1 at 2024-12-19 21:27:08 -0500
Processing by BookingsController#create as TURBO_STREAM
Parameters: {"authenticity_token" => "[FILTERED]", "booking" => {"passengers_attributes" => {"0" => {"passport_no" => "1", "name" => "2", "surname" => "3"}, "1" => {"passport_no" => "3", "name" => "4", "surname" => "5"}, "2" => {"passport_no" => "5", "name" => "4", "surname" => "4"}}}, "commit" => "Create Booking"}
TRANSACTION (0.4ms) BEGIN immediate TRANSACTION /*action='create',application='Bookings',controller='bookings'*/
↳ app/controllers/bookings_controller.rb:10:in 'BookingsController#create'
Booking Create (2.1ms) INSERT INTO "bookings" ("created_at", "updated_at") VALUES ('2024-12-20 02:27:08.595012', '2024-12-20 02:27:08.595012') RETURNING "id" /*action='create',application='Bookings',controller='bookings'*/
↳ app/controllers/bookings_controller.rb:10:in 'BookingsController#create'
Passenger Create (0.2ms) INSERT INTO "passengers" ("created_at", "updated_at", "booking_id", "passport_no", "name", "surname") VALUES ('2024-12-20 02:27:08.608990', '2024-12-20 02:27:08.608990', 18, '1', '2', '3') RETURNING "id" /*action='create',application='Bookings',controller='bookings'*/
↳ app/controllers/bookings_controller.rb:10:in 'BookingsController#create'
Passenger Create (0.1ms) INSERT INTO "passengers" ("created_at", "updated_at", "booking_id", "passport_no", "name", "surname") VALUES ('2024-12-20 02:27:08.614035', '2024-12-20 02:27:08.614035', 18, '3', '4', '5') RETURNING "id" /*action='create',application='Bookings',controller='bookings'*/
↳ app/controllers/bookings_controller.rb:10:in 'BookingsController#create'
Passenger Create (0.1ms) INSERT INTO "passengers" ("created_at", "updated_at", "booking_id", "passport_no", "name", "surname") VALUES ('2024-12-20 02:27:08.620005', '2024-12-20 02:27:08.620005', 18, '5', '4', '4') RETURNING "id" /*action='create',application='Bookings',controller='bookings'*/
↳ app/controllers/bookings_controller.rb:10:in 'BookingsController#create'
TRANSACTION (13.3ms) COMMIT TRANSACTION /*action='create',application='Bookings',controller='bookings'*/
↳ app/controllers/bookings_controller.rb:10:in 'BookingsController#create'
Redirected to http://localhost:3000/bookings/18
Completed 302 Found in 75ms (ActiveRecord: 15.9ms (4 queries, 0 cached) | GC: 0.0ms)
❯ rails c
Loading development environment (Rails 8.0.1)
bookings(dev)> Booking.last.passengers
Booking Load (0.1ms) SELECT "bookings".* FROM "bookings" ORDER BY "bookings"."id" DESC LIMIT 1 /*application='Bookings'*/
Passenger Load (0.1ms) SELECT "passengers".* FROM "passengers" WHERE "passengers"."booking_id" = 18 /* loading for pp */ LIMIT 11 /*application='Bookings'*/
=>
[#<Passenger:0x00007d3f2b33ee50
id: 7,
created_at: "2024-12-20 02:27:08.608990000 +0000",
updated_at: "2024-12-20 02:27:08.608990000 +0000",
booking_id: 18,
passport_no: "1",
name: "2",
surname: "3">,
#<Passenger:0x00007d3f2b31bc20
id: 8,
created_at: "2024-12-20 02:27:08.614035000 +0000",
updated_at: "2024-12-20 02:27:08.614035000 +0000",
booking_id: 18,
passport_no: "3",
name: "4",
surname: "5">,
#<Passenger:0x00007d3f48c27bd0
id: 9,
created_at: "2024-12-20 02:27:08.620005000 +0000",
updated_at: "2024-12-20 02:27:08.620005000 +0000",
booking_id: 18,
passport_no: "5",
name: "4",
surname: "4">]
Edits: Per comment, I changed to use form_with
and build the passengers in the controller. You could refactor further but this fixes the bug.