I'm following this tutorial - http://alagram.github.io/blog/2013/11/04/uploading-multiple-files-with-carrierwave-and-a-nested-form/
And receive this error:
TypeError at /products
no implicit conversion of String into Integer
Better errors highlights this line:
#product = Product.new(product_params)
This is my POST log:
Started POST "/products" for ::1 at 2015-08-13 11:59:24 +0300
Processing by ProductsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"sYWHbXoRQtxsUTU+JmJ4fZagrSoQjE779QPrgQdI1Fn0C0O2sa5vPdU7CSnMSkbH2ulDQcTjrKknFLPEIw7NXw==", "product"=>{"title"=>"hdgsdf", "description"=>"", "advertising_text"=>"", "fancy_quote"=>"", "category_id"=>"2", "subcategory_id"=>"1", "volume_ids"=>[""], "options_attributes"=>{"0"=>{"price"=>"", "size"=>"", "weight"=>"", "material"=>""}, "option_pics_attributes"=>[{"product_image"=>"00026708.jpg"}, {"product_image"=>"475614.jpg"}]}}, "commit"=>"Create Product"}
Unpermitted parameter: 0
Completed 500 Internal Server Error in 2ms
I assume, that general logic is correct since there are indeed two images in my params. My further assumption is, that my strong params are somewhat not correct. I've read, that to allow arrays you should use a hash in the strong params and to allow hash you should use an array. I tried all possible combinations and right now I'm confused.
Strong params:
def product_params
params.require(:product).permit(
:title, :description, :advertising_text, :fancy_quote, :product_size_ids, { volume_ids: [] }, :category_id, :subcategory_id,
options_attributes: [:size, :weight, :price, :material, :product_id,
option_pics_attributes: [ :product_image, :option_id ] ])
end
Rest of my code for the record:
new.html.haml - form
=form_for #product, url: products_path do |f|
%p
=f.label :title
=f.text_field :title
%br
=f.label :description
=f.text_area :description
%br
=f.label :advertising_text
=f.text_area :advertising_text
%br
=f.label :fancy_quote
=f.text_area :fancy_quote
%br
=f.label :category_id
=f.collection_select :category_id, Category.all, :id, :title, { prompt: 'Please select category' }
%br
=f.label :subcategory_id
=f.collection_select :subcategory_id, Subcategory.all, :id, :title, { prompt: 'Please select sub-category' }
%br
=f.label 'Product Size'
=f.collection_check_boxes(:volume_ids, Volume.all, :id, :value)
%p
= f.fields_for :options do |builder|
=builder.label :price
=builder.text_field :price
%br
=builder.label :size
=builder.text_field :size
%br
=builder.label :weight
=builder.text_field :weight
%br
=builder.label :material
=builder.text_field :material
%br
=fields_for :option_pic, OptionPic.new, html: { multipart: true, id: "fileupload" } do |op|
= op.label 'Upload image'
= op.file_field :product_image, multiple: true, name: "product[options_attributes][option_pics_attributes][][product_image]", id: 'fileupload'
=f.submit id: 'submit-data'
products.coffee:
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/
jQuery ->
$('#fileupload').fileupload()
add: (e, data) ->
data.context = $("#submit-data")
data.submit()
Models:
class OptionPic < ActiveRecord::Base
mount_uploader :product_image, ProductImageUploader
belongs_to :option
end
class Option < ActiveRecord::Base
belongs_to :product
has_many :option_pics
accepts_nested_attributes_for :option_pics
end
class Product < ActiveRecord::Base
belongs_to :category
belongs_to :subcategory
has_many :options
has_many :voluminazations
has_many :volumes, through: :voluminazations
accepts_nested_attributes_for :options
end
The basic logic for my app is following: I have a product (say, a t-shirt "mercury t-bone", this t-shirt can have many options (say, green, blue, yellow) and for each option I want to upload multiple pics (thus, option_pics - say 4 pics for the green option, 5 for blue option and 2 pictures for the yellow option)
Related
I'm having almost exactly the same problem as here:
Rails has_many :through saving additional fields
However I can't figure out what's missing in my code. The basic structure is following: there are Products and they may have additional services (like cleaning or sharpening). The basic price for each service is same, however products may have different coefficients. Let's say, Product 1 may have sharpening basic price * 1.5 and Product 2 may have sharpening basic price * 2. The join table is called somewhat stupid - servization, I couldn't figure out a decent name.
products_controller.rb:
def update
#product.update!(product_params)
redirect_to #product
end
def product_params
params.require(:product).permit(
:title, :description, :advertising_text, :fancy_quote, :hot, :hotpic, :product_size_ids,
{ volume_ids: [] }, { color_ids: [] }, { addservice_ids: [] }, :category_id, :subcategory_id,
options_attributes: [:size, :weight, :price, :material, :product_id],
images_attributes: [ :image, :product_id ],
servizations_attributes: [:coefficient, :product_id, :addservice_id]
)
end
product model (i cut out the irrelevant parts) :
class Product < ActiveRecord::Base
has_many :servizations
has_many :addservices, through: :servizations
accepts_nested_attributes_for :servizations
before_destroy :ensure_not_referenced_by_any_line_item
end
additional service model:
class Addservice < ActiveRecord::Base
has_many :servizations
has_many :products, through: :servizations
end
servization model
class Servization < ActiveRecord::Base
belongs_to :product
belongs_to :addservice
end
Here is my edit.html.haml:
=form_for #product do |f|
=f.label 'Title'
=f.text_field :title
%p
=f.label 'Colors'
=collection_check_boxes(:product, :color_ids, Color.all, :id, :value )
%p
=f.label 'Additional services'
=collection_check_boxes(:product, :addservice_ids, Addservice.all, :id, :title)
=f.fields_for :servization do |s|
=s.label 'Coefficient'
=s.text_field :coefficient
%p
=f.submit class: 'btn btn-default'
I have no issues saving plain services or just the colors, however when I try to edit and save the coefficient, then I receive following error:
Processing by ProductsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"jLN59PIOgpaIQtg2/rVYcK/xUOCFzSMLosfUhOslY9tNNhjFbh16Aa1/qT/8u7LHuhNTLGr2SB7b/BitekuDJQ==", "product"=>{"title"=>"Power MI", "color_ids"=>["1", "2", "3", ""], "addservice_ids"=>["1", ""], "servization"=>{"coefficient"=>"1.5"}}, "commit"=>"Update Product", "id"=>"7"}
Product Load (0.3ms) SELECT "products".* FROM "products" WHERE "products"."id" = $1 LIMIT 1 [["id", 7]]
Unpermitted parameter: servization
Servization structure:
Servization(id: integer, product_id: integer, addservice_id: integer, coefficient: float, created_at: datetime, updated_at: datetime)
I don't get it... my options are saved well, images too. But additional services refuse to save.
Edit 1:
I think I might be close to the solution. The problem is the correct linking in the form. This is what I came up with so far:
=form_for #product do |f|
%p
=f.label "Additional Services"
%br
-Addservice.all.each do |addservice|
=check_box_tag "product[addservice_ids][]", addservice.id, #product.addservice_ids.include?(addservice.id)
=addservice.title
=text_field_tag "product[servizations][coefficient][]", addservice.servizations.where(product_id: #product.id)[0].coefficient
%br
%p
=f.submit class: 'btn btn-default'
This is the patch request:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"wD8uMSu0TxvumAWcs1V6GtAFeNgYhbEPEh8HuV8RWucBuk8At6e3jMuldJWxW5Ctxed7FPe+2hprJMuQzn+6GQ==", "product"=>{"title"=>"Флешеченка на тристапяцот гигов", "color_ids"=>["1", "2", "3", ""], "addservice_ids"=>["1", "2"], "servizations"=>{"coefficient"=>["4", "5"]}}, "commit"=>"Update Product", "id"=>"5"}
However now I'm getting again
Unpermitted parameter: servizations
Error
The problem is here in this line
=f.fields_for :servization do |s|
which should be
=f.fields_for :servizations do |s|
Update:
You should permit :id for update to work correctly
def product_params
params.require(:product).permit(
:title, :description, :advertising_text, :fancy_quote, :hot, :hotpic, :product_size_ids,
{ volume_ids: [] }, { color_ids: [] }, { addservice_ids: [] }, :category_id, :subcategory_id,
options_attributes: [:size, :weight, :price, :material, :product_id],
images_attributes: [ :image, :product_id ],
servizations_attributes: [:id, :coefficient, :product_id, :addservice_id]
)
end
I've followed Cocoon's instructions for creating a nested form for a has_many association but there's one thing that I can't seem to figure out. I'm able to add an existing genre to my project but when it comes to creating a completely new one the parameters for my genres aren't being permitted. When I refer to my rails console to debug the issue this is the message I receive upon update:
Unpermitted parameters: genre_attributes
What's strange is I've permitted both my join table genreships and my genre model along with having my movie model accept their nested attributes but still no luck. There has to be something I'm not doing in my movie_params method but not able to find what. I've literally tried to permit every possible attribute within this method and completely out of ideas on what could be the solution
Models
class Movie < ActiveRecord::Base
has_many :genreships
has_many :genres, through: :genreships
accepts_nested_attributes_for :genres, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :genreships, :reject_if => :all_blank, :allow_destroy => true
extend FriendlyId
friendly_id :title, use: :slugged
end
class Genreship < ActiveRecord::Base
belongs_to :movie
belongs_to :genre
accepts_nested_attributes_for :genre, :reject_if => :all_blank
end
class Genre < ActiveRecord::Base
has_many :genreships
has_many :movies, through: :genreships
extend FriendlyId
friendly_id :name, use: :slugged
end
Movies Controller
def movie_params
params.require(:movie).permit(:title, :release_date, :summary, genreships_attributes: [:id, :genre_id, :_destroy], genres_attributes: [:id, :_destroy, :name])
end
Form
= simple_form_for #movie do |f|
.field
= f.input :title
.field
= f.input :release_date, label: 'Release Date', order: [:month, :day, :year], start_year: 1901
.field
= f.input :summary, as: :text
#genres
= f.simple_fields_for :genreships do |genreship|
= render 'genreship_fields', :f => genreship
= link_to_add_association 'add a genre', f, :genreships
.actions = f.submit 'Save', class: 'btn btn-default'
Genreship Partial
.nested-fields
#genre_from_list
= f.association :genre, :collection => Genre.order(:name), :prompt => 'Choose an existing genre'
= link_to_add_association 'or create a new genre', f, :genre
= link_to_remove_association "remove genre", f
Genre Partial
.nested-fields
= f.input :name
Here's what's shown in the rails console right after posting a new movie:
Started POST "/movies" for 127.0.0.1 at 2014-11-19 08:17:42 -0500
Processing by MoviesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"2/wqXhn/x73AdOOrSo49Q/OPAmrcVk0rLViJJNpIhps=", "movie"=>{"title"=>"", "release_date(2i)"=>"11", "release_date(3i)"=>"19", "release_date(1i)"=>"2014", "summary"=>"", "genreships_attributes"=>{"1416403056574"=>{"genre_attributes"=>{"name"=>"Comedy"}, "genre_id"=>"", "_destroy"=>"false"}}}, "commit"=>"Save"}
Unpermitted parameters: genre_attributes
You use genre_attributes while genres_attributes (plural form) is defined in your controller. Change it to:
def movie_params
params.require(:movie).permit(:title, :release_date, :summary, genreships_attributes: [:id, :genre_id, :_destroy], genre_attributes: [:id, :_destroy, :name])
end
I created a solution that in a sense deviates from Nathan's example code somewhat but gets the job done nonetheless. Simply moving this line from genreship_fields.html.slim to _form.html.slim did the trick:
link_to_add_association 'create a new genre', f, :genres
My partials now look like this:
Form
= simple_form_for #movie do |f|
.field
= f.input :title
.field
= f.input :release_date, label: 'Release Date', order: [:month, :day, :year], start_year: 1901
.field
= f.input :summary, as: :text
#genres
= f.simple_fields_for :genreships do |genreship|
= render 'genreship_fields', :f => genreship
| #{link_to_add_association 'add a genre', f, :genreships} | #{link_to_add_association 'create a new genre', f, :genres}
.actions = f.submit 'Save', class: 'btn btn-default'
Genreship Partial
.nested-fields
#genre_from_list
= f.association :genre, :collection => Genre.order(:name), :prompt => 'Choose an existing genre'
= link_to_remove_association "remove genre", f
Genre Partial
.nested-fields
= f.input :name
= link_to_remove_association "remove form", f
I know there's a lot of open questions regarding this around here already but none really helped me.
My main model is being saved properly, all the data is in the post-data, but it just doesn't save the nested models!
Thanks heaps.. Is it anything about the permitted parameters or attr_accessors? Something I suspect.. I didn't really find any documentation on that..
This is my code:
products_controller.rb
def product_params
params.require(:product).permit(:title, :description, :netprice, :vat, :price, :unit_count, :unit, :category, :img, :origin, :originid, :producer, :stock, :discount, :stories_attributes, :origins_attributes, :id)
end
end
def new
#product = Product.new
3.times { #product.stories.build }
#product.origins.build
#categories = []
Category.all.each do |category|
#categories.push([category.name, category.name])
end
end
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
format.json { render action: 'show', status: :created, location: #product }
else
format.html { render action: 'new' }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
origin.rb
class Origin < ActiveRecord::Base
belongs_to :product
end
product.rb
class Product < ActiveRecord::Base
attr_accessor :stories_attributes, :origins_attributes
has_many :stories
has_many :origins
accepts_nested_attributes_for :stories, allow_destroy:true, :reject_if => lambda { |a| a[:text].blank?}
accepts_nested_attributes_for :origins, allow_destroy:true, :reject_if => lambda {|a| a[:city].blank?}
story.rb
class Story < ActiveRecord::Base
belongs_to :product
end
relevant part of the form
<section class="product-story-section">
<%= f.fields_for :stories do |builder| %>
<fieldset>
<%= builder.label :yindex, "Y-Index" %><br>
<%= builder.text_field :yindex %><br>
<%= builder.label :description, "Story-Text" %><br>
<%= builder.text_area :description %><br>
<%= builder.label :img, "Bild-URL" %><br>
<%= builder.text_field :img %><br>
<%= builder.hidden_field :productid, value:"1" %><br>
</fieldset>
<% end %>
</section>
<hr>
<%= f.fields_for :origins do |builder| %>
<fieldset>
<%= builder.label :city, "Stadt" %><br>
<%= builder.text_field :city %><br>
<%= builder.label :country, "Land" %><br>
<%= builder.text_area :country %><br>
<%= builder.label :geolat, "Geolocation Latitude" %><br>
<%= builder.text_field :geolat %><br>
<%= builder.label :geolng, "Geolocation Longitude" %><br>
<%= builder.text_field :geolng %><br>
</fieldset>
<% end %>
Started POST "/products" for 127.0.0.1 at 2013-12-24 21:01:16 +0100
Processing by ProductsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"90MXIlzOQg6AcTwwYOkfNsfPuSt1/9UHjqZwHVRnET4=", "product"=> {"title"=>"9", "description"=>"9", "netprice"=>"9", "vat"=>"9", "price"=>"9", "unit_count"=>"9", "unit"=>"9", "img"=>"", "originid"=>"99", "origin"=>"", "producer"=>"9", "stock"=>"9", "discount"=>"9", "stories_attributes"=>{"0"=>{"yindex"=>"9", "description"=>"9", "img"=>"9", "productid"=>"1"}, "1"=>{"yindex"=>"9", "description"=>"9", "img"=>"9", "productid"=>"1"}, "2"=>{"yindex"=>"9", "description"=>"9", "img"=>"9", "productid"=>"1"}}, "origins_attributes"=>{"0"=>{"city"=>"9", "country"=>"", "geolat"=>"9", "geolng"=>"9"}}}, "commit"=>"Create Product"}
Unpermitted parameters: img, productid
Unpermitted parameters: img, productid
Unpermitted parameters: img, productid
(0.1ms) BEGIN
SQL (0.3ms) INSERT INTO `products` (`created_at`, `description`, `discount`, `img`, `netprice`, `origin`, `originid`, `price`, `producer`, `stock`, `title`, `unit`, `unit_count`, `updated_at`, `vat`) VALUES ('2013-12-24 20:01:16', '9', 9.0, '', 9.0, '', 99, 9.0, '9', 9, '9', '9', 9, '2013-12-24 20:01:16', 9.0)
(0.5ms) COMMIT
Redirected to http://localhost:3000/products/1
Completed 302 Found in 46ms (ActiveRecord: 0.9ms)
New error msg
`attr_accessible` is extracted out of Rails into a gem. Please use new recommended protection model for params(strong_parameters) or add `protected_attributes` to your Gemfile to use old one.
You have an extra end in your product_params method. This should have raised an error.
You also need to add stories and origins attributes to the permit list:
def product_params
params.require(:product).permit(:title, :description, :netprice, :vat, :price, :unit_count, :unit, :category, :img, :origin, :originid, :producer, :stock, :discount, stories_attributes: [:yindex, :description, :image], origins_attributes: [ :city, :country, :geolat], :id)
end
Update: Add missing attributes to the permit list on stories_attributes and origins_attributes:
def product_params
params.require(:product).permit(:title, :description, :netprice, :vat, :price, :unit_count, :unit, :category, :img, :origin, :originid, :producer, :stock, :discount, stories_attributes: [:yindex, :description, :img, :product_id], origins_attributes: [ :city, :country, :geolat, :geolng], :id)
end
Second update:
You also need to update your product model as follows:
class Product < ActiveRecord::Base
attr_accessible :stories_attributes, :origins_attributes
...
end
Replace attr_accessor with attr_accessible. attr_accessible should be used to allow attributes to mass assignment.
In your controller's create action:
# ProductsController
def create
#product = Product.new(product_params)
...
end
Your product_params definitely contains stories_attributes and origins_attributes and are passed to the Product.new method correctly, but because their attributes are not allowed to mass assignment you are not seeing associated stories and origins created. Note that attr_accessor, a Ruby method, only defines getters and setters for the attributes but does not allow for mass assignment.
So I'm working on a project where I have a document object (basically an E-Library application) and I have a bunch of Tag objects that I want to be able to associate with it. Currently I have a has_and_belongs_to_many association between the two. My question is in the form for a tag, what is the best way to select from a list of available tags to associate with that document? And will I have to do any fancy work in the controller to make this happen?
I'm using rails 3.2
Here is some of the code:
# This is the text model
# It will not have an attachment but instead it's children will
class Text < ActiveRecord::Base
attr_accessible :name, :author, :date, :text_langs_attributes, :notes
has_many :text_langs, dependent: :destroy
belongs_to :item
validates :author, presence: true
has_and_belongs_to_many :tags
accepts_nested_attributes_for :text_langs
def get_translations
TextLang.where(:text_id => self.id)
end
def get_language(lang)
TextLang.where(:text_id => self.id, :lang => lang).first
end
end
This is the tag:
# This is the Tags class
# It has and belongs to all of the other file classes
# the tags will need to be translated into four langauges
# Tags will also own themselvea
class Tag < ActiveRecord::Base
attr_accessible :creole, :english, :french, :spanish, :cat,
:english_description, :french_description, :spanish_description,
:creole_description, :parent_id
has_and_belongs_to_many :texts
has_and_belongs_to_many :sounds
belongs_to :parent, :class_name => 'Tag'
has_many :children, :class_name => 'Tag', :foreign_key => 'parent_id'
validates :cat, presence: true, inclusion: { in: %w(main sub misc),
message: "%{value} is not a valid type of tag" }
validates :english, :spanish, :french, :creole, presence: true
TYPES = ["main", "sub", "misc"]
end
This is the form:
= form_for #text do |f|
- if #text.errors.any?
#error_explanation
%h2= "#{pluralize(#text.errors.count, "error")} prohibited this text from being saved:"
%ul
- #text.errors.full_messages.each do |msg|
%li= msg
.field
= f.label :name
= f.text_field :name
.field
= f.label :date
= f.date_select :date
.field
= f.label :author
= f.text_field :author
= f.fields_for :text_langs do |pl|
.field
= pl.label :title
= pl.text_field :title
.field
= pl.label :lang
= pl.text_field :lang
.field
= pl.label :description
= pl.text_field :description, :size => 150
.field
= pl.label :plain_text
= pl.text_area :plain_text
.field
= pl.label :published
= pl.check_box :published
.field
= f.label :txt
= f.file_field :txt
.field
= f.label :notes
= f.text_area :notes, :rows => 10
.actions
= f.submit 'Save'
First of all I would suggest to try simple_form gem it would make your forms DRY and simple. They have very nice features for associations.
You would end doing something like this:
= simple_form_for #text do |f|
...
= f.association :tags, as: :check_boxes
Could be check boxes, radio buttons or maybe a select with multiple values if you need it.
Hope it helps
I have seen multiple questions similar to this, but none worked for me.
I have a team model:
class Team < ActiveRecord::Base
has_one :p1, :class_name => "Player", :foreign_key => 'player_id', :validate => true
has_one :p2, :class_name => "Player", :foreign_key => 'player_id', :validate => true
end
in my team's _form.html.erb, I am referring to players as
<%= f.collection_select :p1, Player.all, :id, :name %>
However, on form submission, I see the error:
Player(#28401456) expected, got String(#14111904)
Application Trace | Framework Trace | Full Trace
app/controllers/teams_controller.rb:47:in `new'
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"GSIcEvROFnvgGWT4HvE2VNqRw4NxU1J8iAw/WhZeRLk=",
"team"=>{"p1"=>"1"},
"commit"=>"Create Team"}
And here is the code at line
def create
#team = Team.new(params[:team])
.....
end
Any ideas please?
Finally, this worked:
<%= f.collection_select :p1_id, Player.all, :id, :name %>
Here is the magic:
My migration has t.references p1 and that created a column of p1_id in the database.
When The form is submitted, rails is looking to fill in the id of the reference at:
def create
#team = Team.new(params[:team])
.....
end
I could be wrong, but my guess is that instead of
<%= f.collection_select :p1, Player.all, :id, :name %>
you need
<%= f.collection_select :p1, :team_id, Player.all, :id, :name %>
http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-collection_select
Try this:
<%= f.collection_select :player_id, Player.all, :id, :name %>