Nested form attributes wont save - ruby-on-rails

I am required to use nested forms on an assignment I am working on and I got stuck because my nested form attributes wont submit to database.
Here is what my controller looks like
def new
#booking = Booking.new
params[:no_of_passengers].to_i.times { #booking.passengers.build }
end
def create
#booking = Booking.new(booking_params)
respond_to do |format|
if #booking.save
format.html { redirect_to '/booking_confirmed', 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
private
def booking_params
params.permit(
:airline, :origin, :destination, :departure_date, :departure_time, :arrival_date,
:arrival_time, :flight_id, :price, :no_of_passengers, :user_id, :booking,
passenger_attributes: [
:id,:booking_id, :name, :email,:done,:_destroy
]
)
end
Here is the association between the models
class Booking < ActiveRecord::Base
has_many :passengers
accepts_nested_attributes_for :passengers, reject_if: lambda { |attributes| attributes['name'].blank? }
end
class Passenger < ActiveRecord::Base
belongs_to :bookings
end
And here is the form
<%= form_for #booking do |b| %>
<%= b.fields_for :passengers do |p| %>
<%= p.text_field :name, placeholder: "Passenger Name" %>
<%= p.text_field :email, placeholder: "Passenger Email" %>
<% end %>
<% end %>
I checked the passenger table using Passenger.all in rails console and it returns nothing.
What am I doing wrong?

After a pairing session with sunnyk, I was able to see the errors.
The first error was that my class Passenger has belongs_to :bookings instead of belongs_to :booking. This is a common error though. The Associations between these classes now looks like:
class Booking < ActiveRecord::Base
belongs_to :flight
has_many :passengers
accepts_nested_attributes_for :passengers, reject_if:
lambda {|attributes| attributes['name'].blank?}, :allow_destroy => true
end
class Passenger < ActiveRecord::Base
belongs_to :booking
end
class Flight < ActiveRecord::Base
has_many :bookings
has_many :passengers, through: :bookings
accepts_nested_attributes_for :passengers
accepts_nested_attributes_for :bookings
end
Next:
Instead of using the default value of no_of_passengers for building my nested form, I used the cocoon gem, which makes nested forms building and management easier. I also crated a new params method, in which I made the flight_id permitted, and then passed it as an argument for my booking instance in my new method alongside my current user. So now my new method looks like this.
def new
#booking = Booking.new(new_booking_params)
#booking.user = current_user if current_user
end
def new_booking_params
params.permit(:flight_id)
end
After that, I had to make another params method for my create method, so as to allow the parameters I want in the bookings table, this include the passengers_attributes. Now my create method looks like this.
def create
#booking = Booking.new(another_booking_params)
respond_to do |format|
if #booking.save
format.html { redirect_to '/booking_confirmed', 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
def another_booking_params
params.require(:booking).permit(:flight_id, :user_id, :no_of_passengers,
passengers_attributes:[:name, :email])
end
Lastly, I had to adjust my form to look like this.
<%= form_for(#booking, url: bookings_path) do |f| %>
<%= f.hidden_field(:flight_id)%>
<%= f.hidden_field(:user_id) %>
<%= f.hidden_field(:no_of_passengers)%>
<%= f.fields_for :passengers do |passenger| %>
<%= render 'passenger_fields', :f => passenger %>
<% end %>
<%= link_to_add_association 'Add Another passenger',f, :passengers, :class => 'btn btn-primary add' %>
<%= submit_tag "Book Now", class: "btn btn-primary book" %>
<% end %>
and passenger_fields partial looks like.
<div class="nested-fields form-inline">
<div class="form-group">
<%= f.text_field :name, :class => "form-control", placeholder: "Passenger Name" %>
</div>
<div class="form-group">
<label>-</label>
<%= f.text_field :email, :class => "form-control", placeholder: "Passenger Email" %>
</div>
<div class="links pull-right">
<%= link_to_remove_association "Delete", f, class: "btn btn-danger" %>
</div>
<hr>
</div>
All that did the trick. I hope this will help others to understand nested forms better

Related

what is causing this AssociationTypeMismatch in my controllers?

I have two controllers - ItemsController and TradesController. I'm building a #trade inside the ItemsController #show action, which is sent to the TradesController #create action with a form.
class ItemsController < ApplicationController
def show
#item = Item.friendly.find(params[:id])
#trade = current_user.requested_trades.build
#approved_trades = #item.trades
respond_to do |format|
format.html
format.json { render :json => #items.to_json(:methods => [:image_url]) }
end
end
class TradesController < ApplicationController
def create
#trade = current_user.requested_trades.build(trade_params)
respond_to do |format|
if #trade.save
format.html { redirect_to #trade, notice: "Your request for trade has been submitted. You will be notified once it is approved or denied." }
format.json { render :index, status: :created, location: #trade }
else
format.html { redirect_to #trade, notice: "Pick another amount" }
end
end
end
private
def trade_params
params.require(:trade).permit(:trade_requester, :trade_recipient, :wanted_item, :collateral_item, :shares)
end
end
And then here's my Trade model
class Trade < ActiveRecord::Base
belongs_to :trade_requester, class_name: "User"
belongs_to :trade_recipient, class_name: "User"
belongs_to :wanted_item, class_name: "Item"
belongs_to :collateral_item, class_name: "Item"
end
Here's the form in my Item's show view:
<%= form_for(#trade) do |f| %>
<%= f.hidden_field :wanted_item, value: #item.id %>
<div class="field">
<%= f.text_field :shares, placeholder: "Pick a number between 1 and #{#item.shares}" %>
<%= f.submit "Trade", class: "button minty-button wide-button" %>
</div>
<% end %>
The above code for the ItemsController posts to the TradesController create action, but I'm getting an error that says ActiveRecord::AssociationTypeMismatch in TradesController#createItem(#70095717466760) expected, got String(#70095657672800)
Why is that expecting an Item? It seems that if #trade creation results in error, then it should redirect to #trade.
The quick solution is to change your hidden field from :wanted_item to :wanted_item_id:
<%= form_for(#trade) do |f| %>
<%= f.hidden_field :wanted_item_id, value: #item.id %>
<div class="field">
<%= f.text_field :shares, placeholder: "Pick a number between 1 and #{#item.shares}" %>
<%= f.submit "Trade", class: "button minty-button wide-button" %>
</div>
<% end %>
Also, make sure your trade_params method permits wanted_item_id:
def trade_params
params.require(:trade).permit(:trade_requester, :trade_recipient, :wanted_item_id, :collateral_item_id, :shares)
end
You may have a similar issue with :collateral_item in another form.

undefined method `facilities' for nil:NilClass - Nested actions

I have an "undefined method" issue with my app, and don't find where it comes from :(.
In my app, i have 4 models :
Deal, Pool (which belongs to deal), Facility (which belongs to pool), Facilityschedule (which belongs to Facility).
class Deal < ActiveRecord::Base
has_many :pools, :dependent => :destroy
accepts_nested_attributes_for :pools, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
end
class Pool < ActiveRecord::Base
belongs_to :deal
has_many :facilities, :dependent => :destroy
accepts_nested_attributes_for :facilities, :allow_destroy => true
end
class Facility < ActiveRecord::Base
belongs_to :pool
has_many :facilityschedules, :dependent => :destroy
accepts_nested_attributes_for :facilityschedules, :reject_if => lambda { |a| a[:date].blank? }, :allow_destroy => true
end
class Facilityschedule < ActiveRecord::Base
belongs_to :facility
end
I have a partial form which allows the user to create all of these :
<%= form_for(#deal) do |f| %>
<% if #deal.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#deal.errors.count, "error") %> prohibited this deal from being saved:</h2>
<ul>
<% #deal.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name, "Deal name"%>
<%= f.text_field :name %>
<br/>
</div>
<div>
<%= f.fields_for :pools do |builder|%>
<%= builder.label :name, "Pool name" %>
<%= builder.text_field :name, :rows => 3 %>
<%= builder.check_box :_destroy %>
<%= builder.label :_destroy, "Remove Pool" %>
<br/>
<%= builder.fields_for :facilities do |fbuilder|%>
<%= fbuilder.label :name, "Facility name" %>
<%= fbuilder.text_field :name, :rows => 3 %>
<%= fbuilder.check_box :_destroy %>
<%= fbuilder.label :_destroy, "Remove Facility" %>
<br/>
<%= fbuilder.fields_for :facilitieschedules do |sbuilder|%>
<%= sbuilder.label :date, "Schedule" %>
<%= sbuilder.text_field :date, :rows => 3 %>
<%= sbuilder.check_box :_destroy %>
<%= sbuilder.label :_destroy, "Remove Schedule" %>
<br/>
</div>
<% end %>
<% end %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
And finally, i have my Deal controller where the issue is located (new action) :
class DealsController < ApplicationController
before_action :set_deal, only: [:show, :edit, :update, :destroy]
# GET /deals
# GET /deals.json
def index
#deals = Deal.all
end
# GET /deals/1
# GET /deals/1.json
def show
end
# GET /deals/new
def new
#deal = Deal.new
2.times do
pool = #deal.pools.build
2.times do
**facility = #pool.facilities.build**
1. times { facility.facilityschedules.build }
end
end
end
# GET /deals/1/edit
def edit
end
# POST /deals
# POST /deals.json
def create
#deal = Deal.new(deal_params)
respond_to do |format|
if #deal.save
format.html { redirect_to #deal, notice: 'Deal was successfully created.' }
format.json { render :show, status: :created, location: #deal }
else
format.html { render :new }
format.json { render json: #deal.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /deals/1
# PATCH/PUT /deals/1.json
def update
respond_to do |format|
if #deal.update(deal_params)
format.html { redirect_to #deal, notice: 'Deal was successfully updated.' }
format.json { render :show, status: :ok, location: #deal }
else
format.html { render :edit }
format.json { render json: #deal.errors, status: :unprocessable_entity }
end
end
end
# DELETE /deals/1
# DELETE /deals/1.json
def destroy
#deal.destroy
respond_to do |format|
format.html { redirect_to deals_url, notice: 'Deal was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_deal
#deal = Deal.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def deal_params
params.require(:deal).permit(:name, pools_attributes: [:id, :name, :number, :deal_id, :_destroy, facilities_attributes: [:id, :name, :pool_id, :_destroy, facilityschedules_attributes: [:id, :facility_id, :date, :_destroy]]])
end
end
When i try to create a new deal, the following error message pops up "undefined method `facilities' for nil:NilClass" (in bold in the Deal controller, above).
What am i doing wrong?
Many thanks and have a good week end :)
# GET /deals/new
def new
#deal = Deal.new
2.times do
pool = #deal.pools.build
2.times do
**facility = #pool.facilities.build**
1. times { facility.facilityschedules.build }
end
end
end
In the code above you have never set the #pool variable, but instead set pool.
Undefined method for Nil::NilClass is never about not having declared the method, it's that you're trying to call it on a nil object, which is an object of NilClass.
There are also some strange patterns in this method. I've never seen the ** around code in Rails. Are you trying to comment out the line?
Would this work better?
# GET /deals/new
def new
#deal = Deal.new
2.times do
#pool = #deal.pools.build
2.times do
facility = #pool.facilities.build
facility.facilityschedules.build
end
end
end

Rails 4: checkbox and has_many through

This example has been taken from Rails 4 Form: has_many through: checkboxes
models:
#models/user.rb
class User < ActiveRecord::Base
has_many :animals, through: :animal_users
has_many :animal_users
end
#models/animal.rb
class Animal < ActiveRecord::Base
has_many :users, through: :animal_users
has_many :animal_users
end
#models/animal_user.rb
class AnimalUser < ActiveRecord::Base
belongs_to :animal
belongs_to :user
end
The user form:
#views/users/_form.html.erb
<%= form_for(#user) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
# Checkbox part of the form that now works!
<div>
<% Animal.all.each do |animal| %>
<%= check_box_tag "user[animal_ids][]", animal.id, f.object.animals.include?(animal) %>
<%= animal.animal_name %>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Strong params within the users_controller.rb
def user_params
params.require(:user).permit(:name, animal_ids: [])
end
I followed this example and could not save the join table. I have two question here
What type should be animal_ids, string or integer?
How to save the form?
Currently I am saving it like this
def create
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'user was successfully created.' }
format.json { render json: #user, status: :created, location: #user}
else
format.html { render action: "new" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
And this only create user but not the join table. How can I do this?
#user.save is not passing in the nested attributes (animal_ids)
You'll need to pass the params like this:
#user = User.new(user_params)
And in your User model (user.rb) you need to add something like:
accepts_nested_attributes_for :animals
accepts_nested_attributes_for :animal_users

id nil has_many :through but in parameters exist :id

I have 3 models with has_many :through relation. Users, Events, and Galleries. In the method new and create in gallery_controller I need to get the event_id, however I get a nil event_id. But in the mozilla console and in the parameters, there exists the id. I don't what I am doing wrong?
I also want to know if the structure of new and create actions is ok ? I want add a gallery for a event before created and in the same time in the the current_user galleries, i have not can test it by the previous problem.
Thanks and cheers.
class Event < ActiveRecord::Base
has_many: galleries
has_many: users, through: : galleries, : source => : users, : dependent => : destroy
accepts_nested_attributes_for: users
accepts_nested_attributes_for: galleries
end
class User < ActiveRecord::Base
has_many :galleries
has_many :events, through: :galleries, :dependent => :destroy
accepts_nested_attributes_for :events
accepts_nested_attributes_for :galleriesenter code here
end
class Gallery < ActiveRecord::Base
has_many :pictures, :dependent => :destroy
belongs_to :event
belongs_to :user
end
Gallery_controller
def new
#event = Event.find(params[:event_id])
#galery = Gallery.new
respond_to do |format |
format.html# new.html.erb
format.json {
render json: #gallery
}
end
end
def create
#event = Event.find(params[:id])
#gallery = #event.galleries.build(params[:gallery])
#gallery.user = current_user
respond_to do |format |
if# gallery.save
if params[: images]# The magic is here;)
params[: images].each { | image | #gallery.pictures.create(image: image)
}
end
def gallery_params
params.require(:gallery).permit(:description,
:name,
:pictures,
:event_attributes => [],
:user_attributes => [],
)
end
form_ new gallery
<%= form_for [#event,#gallery], :html => { :class => 'form-horizontal', multipart: true } do |f| %>
<div class="control-group">
<%= f.label :name, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :name, :class => 'text_field' %>
</div>
</div>
<div class="control-group">
<%= f.label :description, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :description, :class => 'text_field' %>
</div>
</div>
<div class="control-group">
<%= f.label :pictures, :class => 'control-label' %>
<div class="controls">
<%= file_field_tag "images[]", type: :file, multiple: true %>
</div>
</div>
<div class="form-actions">
<%= f.submit :class => 'btn btn-primary' %>
<%= link_to t('.cancel', :default => t("helpers.links.cancel")),
galleries_path, :class => 'btn btn-mini' %>
</div>
<% end %>
routes
resources :events do
resources :galleries
end
Image error
http://i.stack.imgur.com/mk1Ti.png
The error is because you have a typo in your new method.
This line
#galery = Gallery.new
should be
#gallery = Gallery.new
Furthermore your create method has some mistakes which needs fixing.
def create
#event = Event.find(params[:event_id])
#gallery = #event.galleries.build(gallery_params)
#gallery.user = current_user
respond_to do |format|
if #gallery.save
if params[:images]
params[:images].each { |image| #gallery.pictures.create(image: image)}
end
format.html { redirect_to #gallery, notice: 'Gallery was successfully created.' }
format.json { render json: #gallery, status: :created, location: #gallery }
else
format.html { render :new }
format.json { render json: #gallery.errors, status: :unprocessable_entity }
end
end
end
And also your gallery_params needs tweaking
def gallery_params
params.require(:gallery).permit(:description,:name)
end
You don't want to include :event_attributes => [], :user_attributes => [] unless your form has nested fields for users and events which needs to be saved.
I think I found your problem. To me there seems more problems. But initially to solve your problem: you need add event_id to permit params methods:
params.require(:gallery).permit(:description,
:name,
:pictures,
:event_id, # this line should be here if your foreign key is event_id for gallery model.
:event_attributes => [],
:user_attributes => [],
)
Also your form doesn't content the right instance variable. Differences here:
<%= form_for [#event,#gallery], :html => { :class => 'form-horizontal', multipart: t # you wrote #gallery here but in your controller you wrote:
# controller's action:
#galery = Gallery.new
Suggested way to keep the foreign key hidden as well as you build from events also:
#event = Event.find(params[:event_id])
#gallery = #event.gallaries.build #note: 1.spelling and 2. building from #event object
Then in your form add the foreign key field as hidden:
<%= f.hidden_field :event_id %>

Rails edit adding fields issue

I have a rails 4 application that has an add page and and a edit page. You can add elements easily (there is no issues), but then when you go to edit those and click save, it adds the fields you added initially a second time.
Here is my _form.html.erb
<%= nested_form_for #store do |f| %>
<%= f.fields_for :products do |product_form| %>
<div class='field'>
<%= product_form.text_field :name %>
<%= product_form.hidden_field :_destroy %>
<%= link_to "REMOVE PRODUCT", '#', class: "remove_fields" %>
</div>
<% end %>
<p><%= f.link_to_add "Add PRODUCT", :products %></p>
<%= f.submit 'Save', :class => "primary small" %>
<% end %>
and my store.rb model:
class Store < ActiveRecord::Base
has_many :products, class_name: "StoreProduct"
accepts_nested_attributes_for :products, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
end
my update action in my controller looks like:
def update
respond_to do |format|
if #store.update(store_params)
format.html { redirect_to store_products_path(#store), notice: 'Store was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #store.errors, status: :unprocessable_entity }
end
end
end
What does store_params look like in your controller? If id isn't one of the permitted values, then you can start to see the nested models created as new records each time the update action occurs. You would want to have something like:
params.require(:store).permit(products_attributes: [:id, :name, :_destroy])
See the documentation on strong parameters for the nested_form gem.

Resources