Rails - Parameters not being saved when using nested forms in Cocoon - ruby-on-rails

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

Related

Triple nested form multiple image upload using jquery and carrierwave

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)

nested fields_for Rails 4

i have problem permit about double nested fields_for on rals 4. example relationship:
Service - > has_many :product_services
accepts_nested_attributes_for :product_services, allow_destroy: true
Product Service -> has_many :foto_product_services
accepts_nested_attributes_for :foto_product_services, allow_destroy: true
Service -> new.html.slim
= form_for #service, :html => {:multipart => true} do |f|
= f.fields_for :product_services do |builder|
= render 'field_product', f: builder
= f.submit
Service -> _field_product.html.slim
= f.text_field :price, :class => 'text_field input-lg width-100'
= f.fields_for :foto_product_services do |builder|
= builder.file_field "avatar[]", type: :file, multiple: true
Permit Params in my Service Controller
def service_params
params.require(:service).permit(:service_category_id, :title, :description, :product_services_attributes => [:title, "_destroy"], :foto_product_services_attributes => [:avatar])
end
When I click button submit after selected foto product, I get error unpermitted param :foto_product_services_attributes.
You have deep nested attributes, so the foto_product_services_attributes should be inside the product_services_attributes:
params.require(:service).permit(:service_category_id, :title, :description, :product_services_attributes => [:title, "_destroy", :foto_product_services_attributes => [:avatar]])

Rails: Select multiple tag objects to associate

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

Has_one Association breaking in Edit

I have a nested form using the cocoon gem that gests 5 classes deep. They are all belongs_to and has_many except one that is the 2nd to most nested class with a has_one and belongs_to association. Whenever I edit it, it gets deleted and recreates an instance (not what I want) any ideas?
class FirmwaresController < ApplicationController
def index
#firmwares = Firmware.all
end
def new
#firmware = Firmware.new
end
def edit
#firmware = Firmware.find(params[:id])
end
end
class Setting < ActiveRecord::Base
belongs_to :menu_item
has_many :selections, dependent: :destroy
has_many :dependencies
attr_accessible :kind, :name, :placement, :selections_attributes
accepts_nested_attributes_for :selections, reject_if: :all_blank, allow_destroy: true
validates_presence_of :kind
end
class MenuItem < ActiveRecord::Base
belongs_to :menu
belongs_to :dependency
has_one :setting, dependent: :destroy
attr_accessible :dependency_id, :menu_for, :name, :placement, :setting_attributes
accepts_nested_attributes_for :setting, reject_if: :all_blank, allow_destroy: true
validates_presence_of :name
end
_menu_items.html.haml
.row-fluid
.input-prepend
= f.input :name, label: "Menu Item Name"
.input-append
= f.input :placement, label: "Order in Menu (X.Y)", as: :string
= f.simple_fields_for :setting do |settings_form|
= render 'setting_fields', f: settings_form
%p
= link_to_add_association "Has Setting?", f, :setting, class: "btn btn-primary btn-small add_setting_link"
= link_to_remove_association 'Remove Menu Item', f, class: "btn btn-danger btn-small remove_menu_item"
_setting_fields.html.haml
.row-fluid
.input-prepend
= f.input :kind, label: "Type", collection: setting_kinds, input_html: {class: "setting_type"}
.input-append
= link_to_remove_association 'Remove Setting', f, class: 'btn btn-danger btn-small remove_setting_link'
.setting_selection{style: "display: none;"}
= f.simple_fields_for :selections do |selections_form|
= render 'selection_fields', f: selections_form
%p= link_to_add_association 'Add Selection Option', f, :selections, class: "btn btn-primary btn-small"
I assume the has_one association you're referring to is the has_one :setting in MenuItem. If so, you might try adding update_only: true to your accepts_nested_attributes_for :setting options. From the documentation (emphasis mine):
By default the :update_only option is false and the nested attributes are used to update the existing record only if they include the record's :id value. Otherwise a new record will be instantiated and used to replace the existing one. However if the :update_only option is true, the nested attributes are used to update the record’s attributes always, regardless of whether the :id is present.
This question is old, but this could help some people:
The reason the association was always recreated for me, was that I had forgotten to include :id in the permitted parameters of phone_number_attributes! If you forget to do that, rails wont get the id and will recreate a new record to replace the old one.
def user_params
params.require(:user).permit(
:avatar,
:remove_avatar,
{ id_photo_attributes: [:photo, :remove_photo] },
{ phone_number_attributes: [:id, :phone_number] } # dont forget :id here!
)
end

another mass assignment issue with nested attributes

I have campaigns and messages. Campaigns have many messages and messages belongs to campaigns. I setup nested attributes for the models and in my view and i'm trying to create a message associated with the model. Here's the code:
class Campaign < ActiveRecord::Base
attr_accessible :message_id, :name, :group_id, :user_id, :messages_attributes
has_many :messages
belongs_to :group
belongs_to :user
accepts_nested_attributes_for :messages
end
class Message < ActiveRecord::Base
attr_accessible :body, :sent, :sent_at
belongs_to :user
belongs_to :campaign
has_many :responses
end
and the form:
= form_for #campaign, :html => {:class => 'form-horizontal'} do |f|
...removed error output code...
%legend
Enter the campaign information
.field
.control-group
%label.control-label
Campaign Name
.controls
= f.text_field :name
= f.collection_select(:group_id, current_user.groups.all, :id, :name, :include_blank => true)
= f.fields_for :message do |m|
= m.text_area :body, :rows => 3
.form-actions= f.submit "#{params[:action] == 'new' ? 'Create New Campaign' : 'Save Campaign'}", :class => 'btn btn-success'
I know it's probably something really simple but I keep getting a mass assignment issue with message. Here's the error:
ActiveModel::MassAssignmentSecurity::Error (Can't mass-assign protected attributes: message):
app/controllers/campaigns_controller.rb:18:in `update'
and finally the params that get created from the form:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"(removed)", "campaign"=>{"name"=>"Weekly Poker", "group_id"=>"1", "message"=>{"body"=>"s"}}, "commit"=>"Save Campaign", "id"=>"1"}
Because it's a has_many association it should be in plural form:
= f.fields_for :messages do |m|
Also, in the controller you will need:
def new
#campaign = Campaign.new
#campaign.messages.build
end

Resources