I've the structure using Cocoon gem, in my app/views/order/_form.html.erb the code below work fine:
<%= f.collection_select(:drink_id, #drinks, :id, :name, :prompt => "Select a drink") %>
But when I using in _drink_fields.html.erb don't work, return this:
undefined method `drink_id' for # Drink:0x007fd30f9799f8
app/views/orders/_drink_fields.html.erb
<div class='nested-fields'>
<div class="field">
<%= f.collection_select(:drink_id, #drinks, :id, :name, prompt: 'Select a drink') %>
</div>
<%= link_to_remove_association "remove", f %>
</div>
app/views/orders/_form.html.erb
<hr>
<h3>Drinks</h3>
<div id='drinks'>
<%= f.fields_for :drinks do |drink| %>
<%= render 'drink_fields', :f => drink %>
<% end %>
<div class='links'>
<%= link_to_add_association 'add', f, :drinks %>
</div>
</div>
<hr>
app/views/models/order.rb
class Order < ApplicationRecord
belongs_to :drink, optional: true
has_many :drinks
accepts_nested_attributes_for :drinks, allow_destroy: true
end
app/controllers/orders_controller.rb
def set_order
#order = Order.find(params[:id])
end
def set_drink
#drinks = Drink.all
end
# Never trust parameters from the scary internet, only allow the white list through.
def order_params
params.require(:order).permit(:number, :drink_id, drinks_attributes: [:id, :name, :_destroy])
end
Why I received it?
How to do work fine?
Thanks guys!!
When checking your association I think you made a mistake there. I see the following
An order belongs to drink
An order has-many drinks
So then follows: A drink belongs-to order then. So a drink can be in only one order?
My guess is you are missing a join table between order and drinks. So add a simple
class OrderedDrink < ActiveRecord::Base
belongs_to :order
belongs_to :drink
end
You also need the table, so you will have to add a migration to make it (obviously).
(you could an amount there: they ordered 5 cola's for instance).
Then in Order you get
class Order
has_many :ordered_drinks, inverse_of: :order
has_many :drinks, through: :ordered_drinks
accepts_nested_attributes_for :ordered_drinks
end
In Drink you write
class Drink
has_many :ordered_drinks
has_many :orders, through: :ordered_drinks
end
(you might not need those relations, to know in how many orders a drink was/is)
And then instead of editing/managing the drinks association, you edit the ordered_drinks and your partial/collection-select will behave as expected.
The reason it doesn't work is because, there is no drink_id method (Which should be correspondent with drink_id column in database) on #drinks object. You probably want to use :id instead of :drink_id
Thank you everyone that helped to solve this trouble, in especially #nathanvda.
My mistake was forget a table to join drink and order, so I recreated the project and besides that drink and order table, I created a ordereddrink table like #nathanvda answered.
app/models/order.rb
has_many :ordered_drinks, inverse_of: :order
has_many :feeds, through: :ordered_drinks
accepts_nested_attributes_for :ordered_drinks, allow_destroy: true
app/controllers/orders_controller.rb
before_action :set_drink, only: [:new, :edit, :update, :destroy]
def set_drink
#drinks = Drink.all
end
def order_params
params.require(:order).permit(:number, ordered_drinks_attributes: [:id, :order_id, :drink_id, :_destroy, drinks_attributes: [:id, :name, :_destroy]])
end
app/views/orders/_form.html.erb
<hr>
<h3>Drinks</h3>
<div id='ordered_drinks'>
<%= f.fields_for :ordered_drinks do |ordered_drink| %>
<%= render 'ordered_drink_fields', :f => ordered_drink %>
<% end %>
<div class='links'>
<%= link_to_add_association 'add drink', f, :ordered_drinks %>
</div>
</div>
<hr>
app/views/orders/_ordered_drink_fields.html.erb
<div class='nested-fields'>
<div class="field">
<%= f.collection_select(:drink_id, #drinks, :id, :name, include_blank: false) %>
</div>
<%= link_to_remove_association "remove drink", f %>
</div>
If anyone had the same trouble, contact me or send a message.
Related
I have 3 models, a Property has_many units and each unit belongs to a Property. A Unit has_one unit_amenity and a unit_amenity belongs to a unit. The issue is that I need to nest the unit_amenity form which is 2 levels down in the new property form and I'm getting an Unpermitted parameter error. I also see just "unit_amenity"=>{"heat"=>"1"} in the logs instead of "unit_amenity_attributes"=>{"heat"=>"1"}.
Property.rb
class Property < ApplicationRecord
has_many :units, dependent: :destroy
accepts_nested_attributes_for :units, allow_destroy: true
end
Unit.rb
class Unit < ApplicationRecord
belongs_to :property
has_one :unit_amenity, dependent: :destroy
accepts_nested_attributes_for :unit_amenity, allow_destroy: true
end
Unit_amenity.rb
class UnitAmenity < ApplicationRecord
belongs_to :unit
end
I have nested the unit_amenity params within the unit params in properties_controller.rb
# Only allow a list of trusted parameters through.
def property_params
params.require(:property).permit(:city, :state, :zip_code,
units_attributes: [ :id, :_destroy, :unit_number, :bedrooms, :bathrooms, :property_id,
unit_amenity_attributes: [ :id, :_destroy, :unit_id, :heat, :air_conditioning ] ])
end
end
The form has a nested fields_for for unit_amenity
<%= form_with(model: property) do |form| %>
<div class="col-span-6">
<%= form.label :city %>
<%= form.text_field :city %>
</div>
....
<%= form.fields_for :unit_amenity do |amenity_field| %>
<div>
<%= amenity_field.check_box :heat, class: '', type: 'checkbox', id: 'heat' %>
<%= amenity_field.label :heat, class: "text-sm font-medium text-gray-700" %>
</div>
....
<% end %>
<% end %>
The new action of the properties_conroller.rb
def new
#property = Property.new
units = #property.units.build # has_many association
units.build_unit_amenity # has_one association
end
You need to nest the calls to fields for:
<%= form_with(model: property) do |form| %>
# ...
<%= form.fields_for :units do |unit_field| %>
<%= unit_field.fields_for :unit_amenity do |amenity_field| %>
# ...
<% end %>
<% end %>
Aparat from that you should also remove property_id and unit_id from the parameters whitelist. Never have the "parent id" in the params whitelist.
They are assigned when you create nested records and could actually be used to create records belonging to another property if a malicous user felt like it.
I have a many-to-many model ProductCategory product_category (joint-table) and
I'm having issue with nesting the parameter in the ProductsController. The error I keep getting is that its unpermitted params category_ids but I have nested it in the strong product params.
I took a picture of the important parts of the code. Please take a look and let me know thank you. Here is the most important part of the code I think:
<%= form_with(model: [:user, #product], local: true) do |f|%>
<h4>Category</h4>
<div class="dropdown-trigger btn">
<%= f.collection_select(:category_ids, Category.all, :id, :name) %>
</div>
<h4>Product Name:</h4>
<%= f.text_field :name %><br/>
<h4>Product Price:</h4>
<%= f.number_field :price, value: #product.price ? '%.2f' % #product.price : nil, min: 0, step: 0.01 %>$<br/>
<h4>Product Description:</h4>
<%= f.text_field :description %><br/>
<h4>Product Image (recommended)</h4>
<%= f.file_field :image %><br/>
The require in ProductsController:
def product_params
params.require(:product).permit(:name, :price, :description, :image, category_ids: [])
end
And the relevant parts of Product and ProductCategory model.
class Product < ApplicationRecord
belongs_to :user
has_many :product_categories
has_many :categories, though: :product_categories
has_one_attached :image
end
class ProductCategory < ApplicationRecord
belongs_to :product
belongs_to :category
end
class Category < ApplicationRecord
belongs_to :user
has_many :product_categories
has_many :products, though: :product_categories
end
code screenshot
You are receiving an "unpermitted params category_ids" error, because you first need to declare in your Product model the following:
accepts_nested_attributes_for :categories , allow_destroy: true
Once that is done, you should start receiving all the category_ids info, really nested inside your params.
However, I fully recommend DO NOT perform on your views and partials an ActiveRecord query over your DB. For example:
<div class="dropdown-trigger btn">
<%= f.collection_select(:category_ids, Category.all, :id, :name) %>
</div>
That is not advisable. Instead, you should receive from your controller the whole set of categories. The only function on the view in this case is to fill the data by the user, to select the categories, and then after a submit to send all that information back to the controller. That's all. Not performing any kind of query. It's true that you can do it. I mean, it is physically possible to do it there on that view, or even to do it on a helper (also wrong, a helper is to perform additional actions over resources already loaded or received from controllers), but MVC means the separation of duties for several reasons.
Anyway, in your case I would choose to go more or less with something like this:
On products_controller.rb:
def edit
#categories_to_assign = product_service.get_categories_to_assign(#product)
end
def product_service
ProductService
end
def product_params
params.require(:product).permit(:name, :price, :description, :image, categories_to_assign: [])
end
On product_service.rb it gets the categories:
def self.get_categories_to_assign(product)
categories_scope.where.not(id: product.categories.map(&:id)).map do |category|
["#{category.name}", category.id]
end
end
def self.categories_scope()
Category
end
Then on the edit/new view:
<%
categories_to_assign = #categories_to_assign || []
%>
<% content_for :products_main_content do %>
<div id="edit_product_content">
<%= render partial: 'products/form', locals: {
product: product,
return_to: return_to,
categories_to_assign: categories_to_assign
} %>
</div>
<% end %>
Then on the _form.html.erb partial:
<%
categories_to_assign = local_assigns.fetch(:categories_to_assign, [])
%>
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title"><%= t('products.categories.title') %></h2>
</div>
<div class="panel-body">
<div class="form-horizontal" id="categories_container" data-sjr-placeholder>
<%= render partial: 'products/categories', locals: {f: f, categories_to_assign: categories_to_assign} %>
</div>
</div>
</div>
And finally on the _categories.html.erb partial:
<%
categories_to_assign = local_assigns.fetch(:categories_to_assign, [])
%>
<% if categories_to_assign.present? %>
<%= select_tag "#{f.object_name}[categories_to_assign][]", options_for_select(categories_to_assign), {id: "#{f.object_name}_categories_to_assign", include_blank: true, multiple: true, class: 'form-control', data: {placeholder: t('products.form.select_category')}} %>
<% end %>
As you can see, the general idea is passing the pertinent information from the controller, after been properly retrieved on the product_service (you should add it), and then it goes to the edit/new view and then it finally goes down into the nested partials. That way everything is separated in its respective areas of responsibilities.
So im working through the Odin Project's "Flight Booker" project. https://www.theodinproject.com/courses/ruby-on-rails/lessons/building-advanced-forms. Which essentially is what it sounds like and im running into a problem with passing nested attributes.
First and foremost the Relevant Models:
class Booking < ApplicationRecord
belongs_to :passenger
belongs_to :flight
accepts_nested_attributes_for :passenger
end
class Flight < ApplicationRecord
has_many :bookings, dependent: :destroy
has_many :passengers, through: :bookings
belongs_to :to_airport, class_name: 'Airport', foreign_key: 'origin_id'
belongs_to :from_airport, class_name: 'Airport', foreign_key: 'destination_id'
end
class Passenger < ApplicationRecord
has_many :bookings, dependent: :destroy
has_many :flights, through: :bookings
end
The passenger schema just contains an email and name for right now. But the problem is when I pass the information to the "booking" controller. Here is my "New" form for booking.
<%= form_for #booking do |f| %>
<%= f.hidden_field :flight_id, value: params[:booking][:flight_num] %>
<%= f.hidden_field :passengers_num, value: params[:booking][:passengers_num] %>
<% params[:booking][:passengers_num].to_i.times do |passenger| %>
<%= fields_for :passenger do |passenger| %>
<%= passenger.label :name, 'Name', class: "Label" %>
<%= passenger.text_field :name %>
<%= passenger.label :email, 'email', class: "Label" %>
<%= passenger.email_field :email %>
<% end %>
<% end %>
<%= f.submit "Book Flight" %>
<% end %>
(Ignore the hidden fields for now, they are passed from the "Flights" search page and Im getting those just fine.)
So I am getting the multiple forms (name and email fields) but when I "Submit" I am only getting parameters for the last field sets. (So if there are 3 sets of name/email fields, I only get parameters for the last one).
It's possible im not understanding the fields_for however as I can't find a ton of good examples.
Thanks!
There could be many issues with your implementation...I'll layout a few...
Move <% params[:booking][:passengers_num].to_i.times do |passenger| %> logic into the new action of your bookings controller...ie
def new
#booking = Booking.new
3.times { #booking.passengers.new } # or whatever your logic is to display x amount of passenger fields
end
Make sure that in your bookings controller you are permitting the nested attributes like this...
params.require(:booking).permit(passengers_attributes: [:name, :email])
As far as the form, you'll need to treat it like a form within a form (makes sense...nested attributes created from a nested form!) and use the block variable...like this
<ul>
<%= f.fields_for :passengers do |passenger_form| %>
<li>
<%= passenger_form.label :name
<%= passenger_form.text_field :name %>
</li>
<!-- other permitted fields -->
<% end %>
</ul>
I'm developing an app for college where a user can log on & upload details of a hiking trail.
So far everything is working & I have also implemented a nested form for photos in each hiking trail. A user can log-on & create a hike.
I would like to display all the hikes which the user created in there show/profile page but when I've set up the relationships in my database & the has_many & belongs_to options in my model. I've also tried to do this with nested accepts_nested_attributes_for :hikingtrails it does none of this works.
I've checked my database when a hikingtrail is created by a user it is not updating the user_id field in the table.
I'm not sure if I'm approaching this entirely the wrong way, should I be looking at polymorphic associations?
class User < ActiveRecord::Base
attr_accessible :user_name, :email, :password, :password_confirmation, :photos_attributes, :hikingtrails_attributes
has_many :hikingtrails
accepts_nested_attributes_for :hikingtrails, :allow_destroy => :true, :reject_if => :all_blank
class Hikingtrail < ActiveRecord::Base
attr_accessible :description, :name, :looped, :photos_attributes,:directions_attributes, :user_id
has_many :photos
has_many :trails
has_many :directions
belongs_to :user
users/show.html.erb
<div class="page-header">
<h1>Your Profile</h1>
</div>
<p>
<b>username:</b>
<%= #user.user_name %>
</p>
<p>
<b>email:</b>
<%= #user.email %>
</p>
<h4>Small Photos</h4>
<% #user.photos.each do |photo| %>
<%= image_tag photo.image_url(:thumb).to_s %>
<% end %>
<h4>Hiking Trails</h4>
<% #user.hikingtrails.each do |hk| %>
<%= hk.name %>
<% end %>
<%= link_to "Edit your Profile", edit_user_path(current_user), :class => 'btn btn-mini' %>
You didn't add :user_id to your accessible attributes in the Hikingtrail model. Try the following:
attr_accessible :description,
:duration_hours,
:duration_mins,
:name,
:looped,
:addr_1,
:addr_2,
:addr_3,
:country,
:latitude,
:longitude,
:photos_attributes,
:trails_attributes,
:directions_attributes,
:user_id
UPDATE:
After seeing the form code, I think it's probably not necessary to do the above and could potentially also be unsafe. Instead, don't set the user_id through mass assignment, but handle user assignment in your controller like so:
class HikingtrailsController < ApplicationController
# ...
def create
#hikingtrail = Hikingtrail.new(params[:hikingtrail])
#hikingtrail.user = current_user
if #hikingtrail.save
# ...
else
# ...
end
end
end
Hope this helps :)
Currently, an Item belongs_to a Company and has_many ItemVariants.
I'm trying to use nested fields_for to add ItemVariant fields through the Item form, however using :item_variants does not display the form. It is only displayed when I use the singular.
I have check my associations and they seem to be correct, could it possibly have something to do with item being nested under Company, or am I missing something else?
Thanks in advance.
Note: Irrelevant code has been omitted from the snippets below.
EDIT: Don't know if this is relevant, but I'm using CanCan for Authentication.
routes.rb
resources :companies do
resources :items
end
item.rb
class Item < ActiveRecord::Base
attr_accessible :name, :sku, :item_type, :comments, :item_variants_attributes
# Associations
#-----------------------------------------------------------------------
belongs_to :company
belongs_to :item_type
has_many :item_variants
accepts_nested_attributes_for :item_variants, allow_destroy: true
end
item_variant.rb
class ItemVariant < ActiveRecord::Base
attr_accessible :item_id, :location_id
# Associations
#-----------------------------------------------------------------------
belongs_to :item
end
item/new.html.erb
<%= form_for [#company, #item] do |f| %>
...
...
<%= f.fields_for :item_variants do |builder| %>
<fieldset>
<%= builder.label :location_id %>
<%= builder.collection_select :location_id, #company.locations.order(:name), :id, :name, :include_blank => true %>
</fieldset>
<% end %>
...
...
<% end %>
You should prepopulate #item.item_variants with some data:
def new # in the ItemController
...
#item = Item.new
3.times { #item.item_variants.build }
...
end
Source: http://rubysource.com/complex-rails-forms-with-nested-attributes/
try this way
in your item controller new action write
def new
...
#item = # define item here
#item.item_variants.build if #item.item_variants.nil?
...
end
and in item/new.html.erb
<%= form_for #item do |f| %>
...
...
<%= f.fields_for :item_variants do |builder| %>
...
<% end %>
...
...
<% end %>
for more see video - Nested Model Form