Can't convert symbol to integer error with rails simple_form nested attributes - ruby-on-rails

I've seen many similar questions, and looked at the answers, but nothing seems to be helping me and I've been working on this for a while now. The error is 'Can't convert symbol into integer'. My goal is to be able to create multiple sub_groups for each race. I'm just starting with trying to create one for the time being. Here's the relevant code...
** UPDATE **
VIEW
<%= simple_form_for(#race, :url => form_path, :method => form_method, :html => { :class =>
'form-horizontal form-compressed' }) do |f| %>
<fieldset>
<%= f.simple_fields_for :sub_groups do |g| %>
<%= g.input :name, requred: false %>
<%= g.collection_radio_buttons :discount_type,
[['dollars', '$'], ['percent', '%']], :first, :last %>
<%= g.input :discount_amount, :as => :integer, required: false %>
<% end %>
<hr/>
** RACE MODEL**
class Race < ActiveRecord::Base
has_many :sub_groups
accepts_nested_attributes_for :sub_groups
attr_accessible :sub_groups_attributes
** SUB_GROUP MODEL **
class SubGroup < ActiveRecord::Base
belongs_to :race
has_many :race_users
attr_accessible :discount_amount, :discount_type, :display_results, :name
end
PARAMS
after my code update...
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"VihBL4TDT/Lte4YBji/4fp4XvOri1UgUZ8B33wQuCko=", "race"=>
{"sub_group"=>{"name"=>"dfd", "discount_type"=>"dollars", "discount_amount"=>"2"}},
"commit"=>"Next", "wizard"=>"2", "id"=>"13-test5"}
CONTROLLER
class RacesController < ApplicationController
def new
#race = Race.new
#sub_groups = #race.sub_groups.build
#wizard_step = -1
#wizard_step_name = Race.wizard_step_name_from_id #wizard_step
#wizard_mode = true
render :layout => "race_wizard"
end
def update
#race = Race.find params[:id]
#wizard_step = params[:wizard].to_i + 1
#race.wizard_step = #wizard_step
#race.update_attributes(params[:race])
end
So I took advice from answer 1, and switched to using :sub_groups in the view. Now I have a new problem, which is the sub-group fields don't show up at all, despite the fact that I built a sub_groups thing in the #new method.
I'm really stumped on how I can make this happen. This is driving me bonkers. Any help is much appreciated. Thanks!

The way fields_for works is that if you supply a symbol it checks whether your model respond to {given_symbol}_attributes=. If it does the name of sub-fields is {given symbol}_attributes, otherwise just {given_symbol}.
What you need is to add accepts_nested_attributes_for :sub_groups to your Race model. This methods will create a default setter sub_groups_attributes=, which will make fields_for :sub_groups to generate fields with name sub_groups_attributes.
You can also write your own sub_groups_attributes= method, but you need to be sure you know what you're doing there as it might be a little tricky to debug.
Note, that fields_to :sub_groups won't display fields if there are no sub_group associated with given object - you will need to build one in your controller first.

Related

select_check_boxes with an has many through relation and :checked option

I am using collection_check_boxes to create object in a has_many through relation;
here some models:
#emotion.rb
class Emotion < ActiveRecord::Base
has_many :emotional_states
has_many :reports, :through => :emotional_states
end
#report.rb
class Report < ActiveRecord::Base
has_many :emotional_states
has_many :emotions, :through => :emotional_states
[... other "irrelevant" stuff here ...]
end
#emotional_states.rb
class EmotionalState < ActiveRecord::Base
belongs_to :report
belongs_to :emotion
end
As you may understand when I create a Report I also select with a collection_check_box a list of Emotions I want to bind to that report (through the model EmotionalState); Everything works on create (I retrieve the hash values and if #report.save I also create EmotionalStates with the #report.id and #emotion.id.)
But when it cames to edit the Report I would like to edit also the associated EmotionalStates (this means creating new EmotionalStates or deleting old one).
How can I populate the select_check_boxes with ALL the available Emotions having checked that emotions that are alredy associated through the EmotionalStates bojects?
If I write something like:
<%= collection_check_boxes(:report, :emotion_id, #report.emotional_states.map{|e| e.emotion}, :id, :name) %>
I'll get a unchecked checkbox for every alredy associated Emotion.
<%= collection_check_boxes(:report, :emotion_id, Emotion.all, :id, :name, :checked => #report.emotional_states.map{|e| e.emotion}) %>
While this code will correctly returns Emotion.all, but will not check the emotions alredy associated to #report through #report.emotional_states.
I've searched all around the wheb for examples on the usage of :checked options for collection_select_boxes without any results...
any hint?
I did the same once in this way.you can also try :
Emotions:
<% Emotion.all.each do |emotion| %>
<%= check_box_tag 'report[emotion_ids][]' , emotion.id, #report.emotion.include?(emotion) %><%= emotion.name %><br/>
<% end %>
In Controller add :emotion_ids=>[] into strong parameters.
And one line into controller update method:
params[:report][:emotion_ids] ||= []
After coming back to this bug I discovered that the problem was an incorrect use of the .map method, mapping a whole object (e.emotion) instead its id (e.emotion.id).
This easily fixed my problem:
<%= collection_check_boxes(:report, :emotion_id, Emotion.all, :id, :name, :checked => #report.emotional_states.map{|e| e.emotion.id}) %>
Thank you for your help!

Rails 4 NOT updating nested attributes

Issue: Instead of updating nested attributes, they are being created on top of the existing nested attributes when I hit the #update action of the associated features_controller.rb
Likely Cause: I think the problem lies in my lack of understanding in Rails' form_for. I think the breakdown is in my views, how I render the persisting nested attributes, and/or how I fail to specify the nested attribute's id, causing it to simply create a new one
feature.rb
class Feature < ActiveRecord::Base
...
has_many :scenarios
accepts_nested_attributes_for :scenarios,
allow_destroy: true,
reject_if: :all_blank
...
end
features_controller.rb
def update
...
project = Project.find(params[:project_id])
#feature = Feature.find(params[:id])
if #feature.update_attributes(feature_params)
# checking feature_params looks good...
# feature_params['scenarios'] => { <correct object hash> }
redirect_to project
else
render :edit
end
end
...
private
def feature_params
params.require(:feature).permit(:title, :narrative, :price, :eta, scenarios_attributes[:description, :_destroy])
end
_form.html.haml (simplified)
= form_for [#project, #feature] do |f|
...
- if #feature.new_record? -# if we are creating new feature
= f.fields_for :scenarios, #feature.scenarios.build do |builder|
= builder.label :description, "Scenario"
= builder.text_area :description, rows: "3", autocomplete: "off"
- else -# if we are editing an existing feature
= f.fields_for :scenarios do |builder|
= builder.label :description, "Scenario"
= builder.text_area :description, rows: "3", autocomplete: "off"
I'm sure there's a nicer way to achieve the if #feature.new_record? check. I'm also using a few Javascript hooks to create dynamic nested attribute forms (which I've left out), heavily influenced by Railscast #196 Nested Model Form (revised)
I would love a really nice Rails-y implementation of dealing with these sorts of nested forms.
Try adding :id to the :scenario_attributes portion of your feature_params method. You only have the description field and the ability to allow a destroy.
def feature_params
# added => before nested attributes
params.require(:feature).permit(:id, :title, :narrative, :price, :eta, scenarios_attributes => [:id, :description, :_destroy])
end
As #vinodadhikary suggested, you no longer need to check if feature is a new record, since Rails, specifically using the form_for method, will do that for you.
Update:
You don't need to define if #feature.new_record? ... else in your form. It will be taken care by Rails when you use form_for. Rails checks if the action is going to be create or update based on object.persisted?, so, you can update your form to:
= form_for [#project, #feature] do |f|
...
= f.fields_for :scenarios, #feature.scenarios.build do |builder|
= builder.label :description, "Scenario"
= builder.text_area :description, rows: "3", autocomplete: "off"
As #Philip7899 mentioned as a comment in the accepted answer, allowing the user to set the id means that they could "steal" children records belonging to another user.
However, Rails accepts_nested_attributes_for actually checks the id and raises:
ActiveRecord::RecordNotFound:
Couldn't find Answer with ID=5 for Questionnaire with ID=5
Basically the ids are looked for in the children association (again, as said by #glampr). Therefor, the child record belonging to another user is not found.
Ultimately, 401 is the response status (unlike the usual 404 from ActiveRecord::RecordNotFound)
Follows some code I used to test the behaviour.
let :params do
{
id: questionnaire.id,
questionnaire: {
participation_id: participation.id,
answers_attributes: answers_attributes
}
}
end
let :evil_params do
params.tap do |params|
params[:questionnaire][:answers_attributes]['0']['id'] = another_participant_s_answer.id.to_s
end
end
it "doesn't mess with other people's answers" do
old_value = another_participant_s_answer.value
put :update, evil_params
expect(another_participant_s_answer.reload.value).to eq(old_value) # pass
expect(response.status).to eq(401) # pass
end
In conclusion, adding the id to the permitted params as stated above is correct and safe.
Fascinating Rails.

Rails 4- Associations -Tutorial Movielist

Hi I am trying to use Rails on Rest 2 - movielist tutorial with rails 4 and making adjustments as I go for new rails. I am stuck on associations of Movies Roles. I have added to Movie.rb
class Movie < ActiveRecord::Base
has_many :roles, :dependent => :destroy
has_many :people, :through => :roles
validates_presence_of :title
def new_role=(values)
values.each do |i, hash|
unless hash[:name].blank?
roles.create(:person_id => hash[:person_id], :name => hash[:name])
roles.save
end
end
end
def deleted_roles=(values)
values.each do |role_id|
roles.find(role_id).destroy
end
end
end
and also to show _Form.html.rb that I render (excert below)
<b>Add New People</b><br />
<% (1..3).each do |i| %>
<%= select_tag 'movie_new_role_person_id', options_for_select(#people), {
:name => "movie[new_role][#{i}][person_id]"
} %>
<%= text_field_tag 'movie_new_role_name', '', {
:name => "movie[new_role][#{i}][name]"
} %><br />
<% end %>
</p>
<p>
<%= f.submit "Update" %>
</p>
It renders the list to choose from but when i submit - nothing is written to database table.
If I manually enter data in database then it displays on movielist page ie: "Stephen Spielberg - Director" etc...
Any help appreciated- Driving me nuts at this stage
I thought it might be params driven restriction but I do not have a good example of associations style params filter
Thanks
Alan
After debugging including Chicagogrrl's !flag I investigated the params.permits in the movies_Controller again and figured I would have to add the method types to the list of excepted. I could not find detailed info on syntax for this but trial and error paid off.
app/controllers/movies_controllers.rb (excerpt)
......
# Never trust parameters from the scary internet, only allow the white list through.
def movie_params
params.require(:movie).permit(:title,:description, :rating, :role, :deleted_roles=>[],
:new_role=> ['person_id', 'name'])
end
......
The delete_roles=>[] takes the array params and process to delete_roles method in movies.rb
the new_role=>['person_id', 'name'] takes the new_role individual params.
I Hope this saves somebody else some time andIf anybody needs anymore info just ask thanks again Alan

RecordNotFound with accepts_nested_attributes_for and belongs_to

I get
ActiveRecord::RecordNotFound: Couldn't find Client with ID=3 for Order with ID=
when trying to submit an Order form for an existing client. This happens through the form or the console by typing:
Order.new(:client_attributes => { :id => 3 })
payment_form.html.erb:
<%= semantic_form_for #order, :url => checkout_purchase_url(:secure => true) do |f| %>
<%= f.inputs "Personal Information" do %>
<%= f.semantic_fields_for :client do |ff| %>
<%= ff.input :first_name %>
<%= ff.input :last_name %>
<!-- looks like semantic_fields_for auto-inserts a hidden field for client ID -->
<% end %>
<% end %>
<% end %>
Order.rb:
class Order < ActiveRecord::Base
belongs_to :client
accepts_nested_attributes_for :client, :reject_if => :check_client
def check_client(client_attr)
if _client = Client.find(client_attr['id'])
self.client = _client
return true
else
return false
end
end
end
The reject_if idea came from here but I logged the method and it's not even being called! It doesn't matter what its name is!
Note: Feb 2020
Since I'm starting to get downvotes on this 8 years later, adding this note. While this was the original solution I went with 8 years ago, a better one has been proposed by MatayoshiMariano (5 years after my OP).
My Original Fix
Fixed the issue by overloading the client_attributes= method, as described here:
def client_attributes=(client_attrs)
self.client = Client.find_or_initialize_by_id(client_attrs.delete(:id))
self.client.attributes = client_attrs
end
If you only want a new Order with an existing client, without modifying the client, you need to assign the id.
Order.new(client_id: 3)
This is another way to do this without overloading the client_attributes= method and cleanest
The new Order now has the client with ID 3
If you also want to update ant client's attributes you must add the client_attributes, for example:
Order.new(client_id: 3, client_attributes: { id: 3, last_order_at: Time.current })
See https://github.com/rails/rails/issues/7256 from 2012.
If you have has_many relationship, this will work. Tested on Rails 6.0.2
def clients_attributes =(attributes)
# Get IDs for any clients that already exist.
client_ids = attributes.values.map { |a| a[:id] }.compact
# Now find them all and move them to this section.
clients << Client.find(client_ids)
# Update them with standard `accepts_nested_attributes_for` behaviour.
super attributes
end
Had the same error creating a new Thing for existing model with has_many and belongs_to relations.
Fixed it by adding a hidden field for the id of the existing model, for instance User, to the form.
= form.input :user_id, as: :hidden
Then new Thing was created without the error.

Ruby/Rails: dynamically change attribute in shared partial

This should be a layup for someone...
I'm trying to change a form field's attribute depending on which controller/model is calling the partial containing the form fields...
The issue (below) is with parent_id... which references one of two columns in a dogs table. It needs to either be kennel_id or master_id depending on which view this partial is being rendered in.
Not comfortable enough, yet, with Ruby/Rails language/syntax/tools to dynamically change this without getting bogged down in if/else statements.
I'm calling a shared partial and passing in a local variable:
= render "dogs/form", :parent => #kennel
or
= render "dogs/form", :parent => #master
In the partial I'd like to:
= form_for ([parent, target.dogs.build]) do |f|
= render "shared/error_messages", :target => parent
.field
= f.label :name
= f.text_field :name
.field
= f.hidden_field :parent_id ### <= PROBLEM
.actions
= f.submit 'Save'
Just thinking out loud:
I don't know if the parent-models have the proper names for it, but you could do something like:
= f.hidden_field "#{parent.class.name.underscore}_id"
But that doesn't look right. So, why not pass it as an argument?
= render "dogs/form", :parent => #master, :foreign_key => :master_id
Or, create aliases on the dog model to handle some sort of dynamic delegation:
class Dog
def parent_id=(parent_id)
case parent.class
when Master then self.master_id = parent_id
when Kennel then self.kennel_id = parent_id
end
end
def parent_id
case parent.class
when Master then self.master_id
when Kennel then self.kennel_id
end
end
end
But that sucks too. Could the relation be polymorphic? Then you can leave out the switching.
class Dog
belongs_to :owner, :polymorphic => true
end
= f.hidden_field :owner_id
Just some ideas. Hopefully one of them makes sense to you...
Wow, my initial answer wasn't even close. I think what you'll want is a polymorphic association: http://guides.rubyonrails.org/association_basics.html#polymorphic-associations This way, the parent can be whatever class you need it to be.

Resources