Rails - Accepts_nested_attributes_for mass assignment error - ruby-on-rails

I am currently trying to set up a form with nested fields on a belongs_to relationship, but I am running into a mass assignment error. My code so far is as follows (some html removed):
Sale model:
class Sale < ActiveRecord::Base
attr_accessible :customer_attributes
belongs_to :customer
accepts_nested_attributes_for :customer
end
new.html.erb:
<div class="container">
<%= form_for :sale, :url => sales_path do |sale| -%>
<%= sale.fields_for :customer do |customer_builder| %>
<%= render :partial => "customers/form", :locals => {:customer => customer_builder, :form_actions_visible => false} %>
<% end -%>
<% end -%>
customers/_form.html.erb
<fieldset>
<label class="control-label">Customer Type</label>
<%= collection_select(:customer, :customer_type_id, CustomerType.all, :id, :value, {}, {:class => "chzn-select"}) %>
</fieldset>
I believe this should allow me to create a Sale object, and a nested Customer object. The parameters being sent are (note some unrelated params are included):
{"utf8"=>"✓",
"authenticity_token"=>"qCjHoU9lO8VS060dXFHak+OMoE/GkTMZckO0c5SZLUU=",
"customer"=>{"customer_type_id"=>"1"},
"sale"=>{"customer"=>{"features_attributes"=>{"feature_type_id"=>"1",
"value"=>"jimmy"}}},
"vehicle"=>{"trim_id"=>"1",
"model_year_id"=>"1"}}
The error I am getting is:
Can't mass-assign protected attributes: customer
I can see why this might be the case, since :customer is not in the attr_accessible list for Sale - though shouldn't the form be sending customer_attributes instead of customer?
Any help / advice appreciated.
EDIT 1: As far as I can tell, attr_accessible in the Sale model should be covered with :customer_attributes - if anyone says different, please let me know.
EDIT 2: I have tried various permutations, but I can not seem to get the parameters to send customer_attributes instead of simply customer - perhaps I have missed a tag or used an incorrect tag somewhere in the forms above?
EDIT 3: I have found another question on SO that indicated a problem with the :url => part on the form_for tag - the question was referring to a formtastic setup, but I'm wondering if that could be what is causing the problem here?

This might be the problem... from the API docs:
Using with attr_accessible
The use of attr_accessible can interfere with nested attributes if
you’re not careful. For example, if the Member model above was using
attr_accessible like this:
attr_accessible :name
You would need to modify it to look like this:
attr_accessible :name, :posts_attributes
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html#label-Using+with+attr_accessible

I got to the answer here eventually. The key was this line:
<%= collection_select(:customer, :customer_type_id, CustomerType.all, :id, :value, {}, {:class => "chzn-select"}) %>
which needed to be changed to:
<%= customer.collection_select(:customer_type_id, CustomerType.all, :id, :value, {}, {:class => "chzn-select"}) %>
Once this was changed, everything fell into place.

Related

Correct way to permit strong parameters for nested attributes in Rails

I have model named note.rb as follows:
class Note < ApplicationRecord
belongs_to :user
has_many :tags
accepts_nested_attributes_for :tags
end
And also a model named tag.rb:
class Tag < ApplicationRecord
belongs_to :note
end
The form for new note creation is as follows:
<%= form_with scope: :note, url: notes_path, local: true do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :description %><br>
<%= f.text_area :description %>
</p>
<%= f.fields_for :tags_attributes do |t| %>
<p>
<%= label_tag(:name, "Add a tag") %><br>
<%= t.text_field :name %>
</p>
<% end %>
<p>
<%= f.submit %>
</p>
<% end %>
<div id= "tag-displayer">
<span id= "tags"></span>
</div>
I am trying create a record for tag with a record of note.
In my notes_controller.rb I have
def create
#note = Note.new(note_params)
#note.user = current_user
if #note.save
redirect_to '/notes'
else
render 'new'
end
end
and :
private
def note_params
params.require(:note).permit(:title, :description, tags_attributes: [:id, :name])
end
Now on form submit I get the following:
TypeError (no implicit conversion of Symbol into Integer):
I get the same error if I use:
params.require(:note).permit(:title, :description, :tags_attributes => [:id, :name])
I get the error:
Unpermitted parameter: :tags_attributes
If I use:
params.require(:note).permit(:title, :description, :tags_attributes => [])
Params for form submit:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"iSsLyOb0ZxZP0rfB4I5yfyrw965zJSLrtkroTUzseY2k4o5DwKpKXlyxN6p99pt4Fwju1RhMZPkbNdv+YVSESQ==", "note"=>{"title"=>"test note with Tag", "description"=>"Test note with tag", "tags_attributes"=>{"name"=>"Rails"}}, "commit"=>"Save Note"}
I am not sure what I'm doing wrong, I've tried all possible solutions available to me.
Using Rails 5 with ruby 2.4.1.
Well, if I look at your params, you actually do have strings. There are several ways to fix that, but the most straightforward way in my opinion is just to permit strings instead of symbols. For tags_attributes, that should be 'tags_attributes' => ..., for the rest I'm not sure if I remember correctly: it's either 'title' or :'title', probably the latter. I think the same goes for 'note' or :'note'. I hope you can just play around a bit and see which one it is. I had the error before, so I'm quite certain that should fix it. I hope I can help! Please let me know which one fixes it, I'm curious to know :)
Well i think you don't need to add id in strong parameters
try this: -
params.require(:note).permit(:title, :description, tags_attributes: [:name])
The immediate issue is that your form is passing:
{..., "tags_attributes"=>{"name"=>"Rails"}}, ...}
When it needs to pass:
{..., "tags_attributes"=>[{"name"=>"Rails"}], ...} # The hash is inside an array.
Or possibly:
{..., "tags_attributes"=>{"any_key_except_id" => {"name"=>"Rails"}}, ...}
I'm pretty sure the TypeError is coming from that issue, probably that at some point it's trying to treat the string "Rails" as a hash and tries to call "Rails"[:id] on it.
My hunch (without reproducing your entire setup) is that this can be solved by changing your line that says:
<%= f.fields_for :tags_attributes do |t| %>
to
<%= f.fields_for :tags do |t| %>
If you look at the documentation for fields_for, it uses the name of the association, without the _attributes at the end. With :tags_attributes, I think the form doesn't know what to do with it, assumes it's a single item instead of a collection, and so doesn't nest the attributes in an array.
Note that if you want to have it display the field for a new tag (instead of just existing tags), I believe you'll have to call #note.tags.build somewhere before the fields_for call so that there's an unsaved Tag entity in the tags collection.
I think it has something to do with your model declaring has_many :tags and your form data coming in as an object :tags_attributes => {id: "", name: ""}.
The TypeError (no implicit conversion of Symbol into Integer): error could be bubbling up when you are trying trying to save the note #note.save or when you are initialising it using #note = Note.new(note_params) but not because of Strong parameters. Strong parameters should permit those parameters because the definition matches the form data you are sending.
Try modifying the frontend to send tags in an array format like :tags_attributes => [{id: "", name: ""}, {id: "", name: ""}]
There is blog that is almost if not exact similar to the problem you are facing, check it out http://billpatrianakos.me/blog/2013/09/29/rails-tricky-error-no-implicit-conversion-from-symbol-to-integer/

Rails 4 - simple_form and pre-populating fields from url

I'm using simple_form and I'd like to pre-populate several fields in my form. In the link to the form I'm passing several values to params in the URL. The trouble comes in when I either try to pass a value to a field that is an integer or an association. In either case, the field does not pre-populate.
Example below...the first two fields populate fine, but I had to force them to be text fields. Maybe that's ok to push the strings from the url into the field, but ideally I'd be able to use either the integer (f.input) or association (f.association). The second two fields don't pull in the param values from the URL.
Any ideas? Thanks in advance!
NOTE - this is for generating a NEW record in the database and not for editing an existing record.
URL: http://localhost:5000/list/new?event_id=4&user_id=11
<!-- These two fields pre-populate -->
<%= f.text_field :event_id, :value => params[:event_id] %>
<%= f.text_field :user_id, :value => params[:user_id] %>
<br>
<!-- These two fields do NOT pre-populate -->
<%= f.association :event_id, :value => params[:event_id] %>
<%= f.input :event_id, :value => params[:event_id], label: 'Event' %>
PS - I'm listening to GusGus' new album on Spotify while working on this and it's helping a lot. :)
Best practice is pre-populate form not with params directly but with ActiveRecord object.
For example you have an AR class:
class Party < ActiveRecord::Base
belongs_to :event
belongs_to :user
end
Then in your controller:
def new
#party = Party.new(party_params)
end
# use strong params to make your parameter more secure;)
def party_params
params.permit(:event_id, :user_id)
end
and then in your edit view:
<%= simple_form_for #party do |f| %>
<%= f.association :event %>
<%= f.association :user %>
<% end %>

Nested forms on rails not displaying correctly on Edit

Hi im using Nested_forms gem for a app, everything is working fine.. Im following the documentation here ...
My form is saving data to database, i can create infinite number of extra fields as i require.
The only problem is when i want to populate the list for example to Edit, then i can´t populate again the list with all the values the user have previously selected, just the 1st value is there , the 2nds select box that should appear, appear transparent.. i leave an image , because english is not my lenguage y probably suck describing it
EDIT: I think the problem is on the loop , because first time when you submit it look like this..
And after saving, and lunching the form again to edit. this is what you get.
Here is the code in there.
<div id="nacionalidad">
<%= f.fields_for :citizens do |citizen_form| %>
<div>
<%= citizen_form.label :citizen, t('generales.citizen') %>
<%= citizen_form.select :country_id , Country.all.collect {|p| [ t("generales."+p.iso), p.id ] }.sort_by {|label,code| label}, { :include_blank => true } , { :class => 'pca33' } %>
<div id="delerr"><%= citizen_form.link_to_remove t('generales.delete') %></div>
</div>
<% end %>
<%= f.link_to_add t('generales.add'), :citizens %>
</div>
And the model
class Citizen < ActiveRecord::Base
attr_accessible :country_id
belongs_to :player
belongs_to :country
end
You might be going about this the wrong way. In my opinion it's much easier to use multiple-select fields and has_many relations. Then everything just works magically!
Form:
<%= select_tag :countries, options_from_collection_for_select(Country.all, 'id', 'name'), :multiple => true %>
Model:
class Citizen < ActiveRecord::Base
attr_accessible :country_id
belongs_to :player
has_many :countries
end
And then if you'd like, you can use another javascript library to make your multiselects more user-friendly:
Select2: http://ivaynberg.github.io/select2/
ChosenJS: http://harvesthq.github.io/chosen/

fields_for builder .object method does not allow me to retrieve object values

I've got 3 models that comprise a has-many-through association.
Model code as follows:
ItemAttrVal Model (the transition table)
class ItemAttrVal < ActiveRecord::Base
belongs_to :attr_name
belongs_to :registry_item
end
RegistryItem Model
class RegistryItem < ActiveRecord::Base
has_many :item_attr_vals
has_many :attr_names, :through => :item_attr_vals
accepts_nested_attributes_for :item_attr_vals, :allow_destroy => :true
end
AttrName Model
class AttrName < ActiveRecord::Base
has_many :item_attr_vals
has_many :registry_items, :through => :item_attr_vals
end
The RegistryItem uses a fields_for as follows:
<%= item.fields_for :item_attr_vals do |iav| %>
<%= render 'item_attr_val_fields', :f => iav %>
<% end %>
In the partial, it looks like this:
<% logger.debug "object type is: #{f.object}"%>
<% logger.debug "some details are: #{f.object.attr_name_id}--"%>
<%= f.select :attr_name_id, options_from_collection_for_select(AttrName.all,"id","description"), :selected => f.object.attr_name_id, :prompt => "Select an attribute" %>
<%= f.text_field :raw_value %> <br />
The 1st 2 debug lines are the bit that my question is about, but it first relates to the 3rd line.
There, I am attempting to provide the dropdown select field with a "pre-selected" value. This is so that when the user is editing the RegistryItem, their previously selected AttrName will be displayed.
I'm attempting to use the f.object.attr_name_id to set that value, however it does not actually properly select the previously selected value, and instead, just goes to the 1st.
The 1st two debug lines were then me trying to make sure that my f.object method worked...
When I looked in my logs, I see the following:
object type is: #<ItemAttrVal:0x007fb3ba2bd980>
some details are: --
Basically, the 1st line shows me that I am getting the ItemAttrVal
The second line does not seem to retrieve any information for it.
I've also used the debugger to check, and in there, I am able to use display f.object.attr_name_id to show me the exact value that I'm expecting...
This kind of comes down to two questions...
Why can't I retrieve the values of f.object?
Am I trying to do line 3 (<%= f.select :attr_name_id, options_from_collection_for_select(AttrName.all,"id","description"), :selected => f.object.attr_name_id, :prompt => "Select an attribute" %>) wrong, and there's actually a better way to do it?
Thanks in advance!
you need to use params[:attr_name_id] into your options_from_collection_for_select
<%= f.select :attr_name_id, options_from_collection_for_select(AttrName.all,"id","description", params[:attr_name_id].to_i), :prompt => "Select an attribute" %>
hope it helps
Turns out I'd placed the :selected in the wrong location...
Original:
<%= f.select :attr_name_id, options_from_collection_for_select(AttrName.all,"id","description"), :selected => f.object.attr_name_id, :prompt => "Select an attribute" %>
Should be:
<%= f.select :attr_name_id, options_from_collection_for_select(AttrName.all,"id","description", f.object.attr_name_id), :prompt => "Select an attribute" %>
Fixing that solved my problem, the attribute names are now appearing as expected for previously saved attributes.
It still doesn't answer my original query about why I'm not able to get the values for f.object printed out, but at least the original-original problem was resolved.

Rails MetaSearch Associations Undefined Method

I am working with a rails app and have begun to work on a search function using metasearch but I am having troubles getting the correct method for the search.
For example I have a model (Proposal) that has a field cost.
..model/proposal.rb
class Proposal < ActiveRecord::Base
belongs_to :user
has_many :users, :through => :invitations
attr_accessible :cost, :user_id
For which this code works fine with meta search
..views/homes/live_requests.html.erb
<%= form_for #search, :url => "/live_requests", :html => {:method => :get} do |
<%= f.label :cost_greater_than %>
<%= f.text_field :cost_greater_than %><br />
<!-- etc... -->
<%= f.submit %>
Yet with a more complicated association I can not manage to get the meta search path correct.
I am trying to search over:
Proposal.last.user.suburb.name #Returns the name of the suburb as expected
I have tried many associations but cannot find the right one.
Proposal has a user_id field which maps to user
So Proposal.user returns a User
User then has a suburb_id which returns a suburb
Suburb has a field called name which returns a string
How would I work this into a metasearch form?
<%= f.text_field :user_user_suburb_name %>
or
<%= f.text_field :user_id_suburb_id_name %>
I cannot come to a solid conclusion.
Thanks for your help in advance.
Was simply following the correct names as declared by the models.
Ie. in the model above, ensuring that it was User, not users etc.

Resources