I'm trying to create #booking and #booking.build_passenger in form_for with nested attributes in Rails 4.2.1
The error I get:
As you see in the console at the bottom of the image:
1. params.require(:booking) returns a Hash-like params for #booking
2. params.class returns ActionController::Parameters
As the params seems to behave correctly, IMO the problem hides somewhere in the form:
<%= form_for #booking do |f| %>
<%= f.hidden_field :flight_id, value: params[:flight_id] %>
<%= render 'flights/flight_info' %>
<div class="field">
<b><%= f.label :num_tickets, "Tickets" %></b>
<%= f.select(:num_tickets, #num_tickets) %>
</div><br>
<h4>Passenger info:</h4>
<%= f.fields_for #booking.build_passenger do |pass| %>
<div class="field">
<%= pass.label :name %>
<%= pass.text_field :name %>
</div>
<div class="field">
<%= pass.label :email %>
<%= pass.email_field :email %>
</div>
<% end %>
<%= f.submit 'Book Flight!' %>
<% end %>
Booking model:
class Booking < ActiveRecord::Base
belongs_to :flight
belongs_to :passenger
accepts_nested_attributes_for :passenger
end
Question: Where and how do I have to edit my code for the app to start creating #booking instances + #booking.build_passenger()
Your booking_params needs to be something like:
def booking_params
params.require(:booking).permit(:flight_id, :num_tickets, passenger_attributes: [:id, :name, :email])
end
First things first
Using:
rails4
oracle enhanced adapter rails4 branch
I have a many to many relationship mapped on an existing database.
My models look as such:
class EventMap < ActiveRecord::Base
self.table_name="TAKE_PART"
self.primary_key="id"
belongs_to :event, foreign_key: "lottery_event_id"
belongs_to :entrant, foreign_key: "address_id"
end
class Event < ActiveRecord::Base
self.table_name="THE_EVENT"
self.primary_key="id"
has_many :event_maps, :foreign_key => "lottery_event_id"
has_many :entrants, :through => :event_maps
accepts_nested_attributes_for :entrants, :reject_if => :all_blank
end
class Entrant < ActiveRecord::Base
self.table_name="ADDRESSES"
self.primary_key="id"
self.set_date_columns :date_of_birth
has_many :events, :through => :event_maps
has_many :event_maps, :foreign_key => "address_id"
end
static page controller for my voting page
...
def vote
#event=Event.find_by(id: 4227)
#entrants=#event.entrants
#entrant=#event.entrants.build
end
...
vote view:
<%= form_for(#event) do |f| %>
<%= f.fields_for :entrant do |builder| %>
<%= render "/entrants/fields", :f => builder %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
entrant/fields partial:
<% if #entrant.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#entrant.errors.count, "error") %> prohibited this entrant from being saved:</h2>
<ul>
<% #entrant.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.hidden_field :id %>
<div class="field">
<%= f.label :lastname %><br>
<%= f.text_field :lastname %>
</div>
<div class="field">
<%= f.label :firstname %><br>
<%= f.text_field :firstname %>
</div>
<div class="field">
<%= f.label :street %><br>
<%= f.text_field :street %>
</div>
<div class="field">
<%= f.label :country_id %><br>
<%= f.number_field :country_id %>
</div>
<div class="field">
<%= f.label :city %><br>
<%= f.text_field :city %>
</div>
<div class="field">
<%= f.label :telephone %><br>
<%= f.text_field :telephone %>
</div>
<div class="field">
<%= f.label :email %><br>
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :date_of_birth %><br>
<%= f.date_select :date_of_birth %>
</div>
<div class="field">
<%= f.label :lang_id %><br>
<%= f.text_field :lang_id %>
</div>
<div class="field">
<%= f.label :added %><br>
<%= f.date_select :added %>
</div>
<div class="field">
<%= f.label :salut %><br>
<%= f.text_field :salut %>
</div>
<div class="field">
<%= f.label :zip %><br>
<%= f.text_field :zip %>
</div>
<div class="field">
<%= f.label :newsletter %><br>
<%= f.check_box :newsletter %>
</div>
<div class="field">
<%= f.label :company %><br>
<%= f.text_field :company %>
</div>
The form now submits to event controller PATCH
class EventsController < ApplicationController
before_action :set_event, only: [:show, :edit, :update, :destroy]
...
# PATCH/PUT /events/1
# --> HOW DO I SAVE THE SUBMITTED ENTRANT HERE??? <--
def update
respond_to do |format|
if #event.update(event_params)
format.html { redirect_to #event, notice: 'Event was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #event.errors, status: :unprocessable_entity }
end
end
end
...
private
# Use callbacks to share common setup or constraints between actions.
def set_event
#event = Event.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def event_params
params.require(:event).permit(:id, :lottery_id, :events_dates_id, :event_date, :event_location, :tickets_for_winner, :prize, :tickets_internally,
:entrants_attributes => [:id, :lastname, :firstname, :street, :country_id, :city, :telephone, :email, :date_of_birth, :lang_id, :added, :salut, :zip, :newsletter, :company])
end
end
How do I register an Entrant with an Event, adding data only to the intermediate model EventMap since the Event will always exist?
Do I need to include accepts_nested_attributes_for in my models to propagate changes accross tables (I could not quite figure out what this does from the documentation)? Do I need to send additional params via the Entrant form to update the EventMap?
Main GOAL: I want a form where Entrants can register to an existing event!
Hard to judge about Your forms, as we don't see any ;)
But You are right to be able to create nested attributes, from nested attributes from, You need to set accepts_nested_attributes_for :some_model, :some_other_model
If You find docs confusing, consult Railscasts:
http://railscasts.com/episodes/196-nested-model-form-part-1
http://railscasts.com/episodes/197-nested-model-form-part-2
You are requiring custom logic and that custom logic must be defined, Rails will not automatically update everything, it must be defined in the update controller as you suggest.
I might do something along the lines of this:
def update
entrant = Entrant.find(params[:entrant_id])
event = Event.find(params[:event_id])
EventMap.create!(event: event, entrant: entrant)
#.... go on with usual stuff
# Alternatively you could use build
event_map = EventMap.new
event_map.build(event: event, entrant: entrant)
end
Hi im trying to create a relation one-to-many in my rails app.
Fist i create my models
class Produto < ActiveRecord::Base
attr_accessible :compra, :descricao, :estoque, :venda
has_many :precos
accepts_nested_attributes_for :precos
end
class Preco < ActiveRecord::Base
attr_accessible :compra_decimal, :produto_id, :venda_decimal
belongs_to :produto
end
Then i created my controller
class ProdutosController < ApplicationController
def new
#produto = Produto.new
#produto.precos.build
end
def create
#produto = Produto.new(params[:produto])
if #produto.save?
redirect_to produtos_path
end
end
end
After this i created my .html.erb pages:
_form
<%= form_for #produto do |f| %>
<p>
<%= f.label :descricao %><br/>
<%= f.text_field :descricao %>
</p>
<p>
<%= f.label :compra %><br/>
<%= f.text_field :compra %>
</p>
<p>
<%= f.label :venda %><br/>
<%= f.text_field :venda %>
</p>
<p>
<%= f.label :estoque %><br/>
<%= f.text_field :estoque %>
</p>
<%= f.fields_for :precos do |builder| %>
<%= render "precos", :f => builder %>
<% end %>
<p><%= f.submit %></p>
<% end %>
_precos
<p>
<%= f.label :venda_decimal %><br/>
<%= f.text_field :venda_decimal %>
</p>
<p>
<%= f.label :compra_decimal %><br/>
<%= f.text_field :compra_decimal %>
</p>
new
<%= render "form" %>
then, when i submit the form this error appears:
ActiveModel::MassAssignmentSecurity::Error in ProdutosController#create
Can't mass-assign protected attributes: precos_attributes
does anyone have any idea about it?
Just change your model:
class Produto < ActiveRecord::Base
attr_accessible :compra, :descricao, :estoque, :venda, :precos_attributes
has_many :precos
accepts_nested_attributes_for :precos
end
I have these two models
class Invoice < ActiveRecord::Base
has_many :items
accepts_nested_attributes_for :items
...
end
class Item < ActiveRecord::Base
belongs_to :invoice
def total
price * quantity
end
...
end
and this nested (!) form that posts to both models:
<h1>Add an Invoice</h1>
<%= form_for #invoice do |f| %>
<p>
<%= f.label :recipient %>
<%= f.text_field :recipient %> </p>
<p>
<%= f.label :date %>
<%= f.text_area :date %>
</p>
<h2>Items</h2>
<p>
<%= f.fields_for(:items) do |f| %>
<%= f.label :description %>
<%= f.text_field :description %>
<%= f.label :price %>
<%= f.text_field :price %>
<%= f.label :quantity %>
<%= f.text_field :quantity %>
<%= f.label :total %>
<%= f.total %><!-- this method call is not working! -->
<% end %>
</p>
<%= f.submit %>
<% end %>
How can I do calculations on my items within the form?
In my Items model I have this method:
def total
price * quantity
end
However, in the form I can't get it to work with f.total. I keep getting this error:
undefined method `total' for #<ActionView::Helpers::FormBuilder:0x10ec05558>
What am I missing here?
You are calling a method not on your model object, but on f, which is a form helper (ActionView::Helpers::FormBuilder). Error message gives a hint to this.
To call on the item, you need to replace
<%= f.total %>
with
<%= f.object.total %>
I have a basic has_many through relationship:
class Foo < ActiveRecord::Base
has_many :bars, :dependent => :destroy
has_many :wtfs :through => :bars
accepts_nested_attributes_for :bars, :wtfs
end
On my crud forms I have a builder block for the wtf, but I need the label to come from the bar (an attribute called label for instance). What's the proper method to do this?
Here's the most simple scaffold:
<h1>New foo</h1>
<% form_for(#foo) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<h2>Bars</h2>
<% f.fields_for :wtfs do |builder| %>
<%= builder.hidden_field :bar_id %>
<p>
<%= builder.text_field :wtf_data_i_need_to_set %>
</p>
<% end %>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
<%= link_to 'Back', foos_path %>
The answer was found in analyzing how the rails FormBuilder works. So in the example above where I need to access the actual wtf object so I can get a property on bar, I need to do the following:
<h2>Bars</h2>
<% f.fields_for :wtfs do |builder| %>
<%= builder.hidden_field :bar_id %>
<p>
<%= builder.label builder.object.bar.data_i_need_for_a_label %>
<%= builder.text_field :wtf_data_i_need_to_set %>
</p>
<% end %>