Saving multiple objects and `stringify_keys' error. Help needed - ruby-on-rails

While I'm trying to save multiple objects, I'm getting the next error:
undefined method `stringify_keys' for #<Array...>
Any ideas about how to deal with it?
My inputs:
Controller:
def create
#course = Course.find(params[:course_id])
#students = #course.users
#students.each do
#grade = #course.grades.build(params[:grade])
end
#grade.each { |a| a.save }
end
_form.html.erb:
<%= form_for ([#course, #grade]) do |f| %>
....
<% #students.each do |a| %>
<tr>
<td><%= a.last_name %><%= a.first_name %>
<%= f.hidden_field :student_id, :value => a.id, :index => #grade.id %></td>
<td><%= f.text_field :grade_evaluation, :index => #grade.id %></td>
<td><%= f.text_field :comment, :index => #grade.id %></td>
</tr>
....
That gives me next params hash for grade:
{....
"grade"=>[
{"student_id"=>"1",
"grade_evaluation"=>"5",
"comment"=>"you are an idiot" },
{"student_id"=>"4",
"grade_evaluation"=>"5",
"comment"=>"you too" }],
"commit"=>"Create",
"course_id"=>"3"}

You are iterating over grades, but not over incomming parameters here:
#students.each do
#grade = #course.grades.build(params[:grade])
end
(notice, that for each element you are passing whole array, not array element).
EVENTUALLY this should look like this (but this is not a nice sollution, but it will fix your problem).
params[:grade].each do |grade_params|
grade = #course.grades.build(grade_params)
end
Take a look at nested attributes, this is definitelly thing your looking for: http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

Related

How to use Rails 5.2 form_with to trigger a specific action?

My application needs to duplicate a Skill (from skills index) as many times the user needs it in his cart. So I decided to trigger the add-to-cart method of the skills_controller when the related form, including the number of duplicates and the Skill's id, is submitted. For this purpose, I added counter to the strong parameters of skills_controller.
Unfortunately, I am missing something to correctly setup the form: when submitted, it triggers the create method. Here is the code:
routes.rb extract
resources :skills, :path => "variables" do
resources :values_lists
member do
post :add_to_cart
get :create_values_list
get :upload_values_list
get :remove_values_list
end
collection do
get :index_all
end
end
skills_controller.rb method
def add_to_cart
#template_skill = Skill.find(params[:id])
iterations = params[:skill][:counter].to_i
until iterations == 0
#skill = #template_skill.deep_clone include: [:translations, :values_lists]
#skill.business_object_id = session[:cart_id]
#skill.template_skill_id = #template_skill.id
#skill.code = "#{#template_skill.code}-#{Time.now.strftime("%Y%m%d:%H%M%S")}-#{iterations}"
#skill.is_template = false
#skill.save
iterations -= 1
end
#business_object = BusinessObject.find(session[:cart_id])
redirect_to #business_object, notice: t('SkillAdded2BO') # 'Skill successfully added to business object'
end
index.html.erb table content
<tbody>
<% #skills.each do |skill| %>
<tr data-href="<%= url_for skill %>">
<% if not session[:cart_id].nil? %>
<td>
<%= form_with model: #skill, :action => "add_to_cart", :method => :post, remote: false do |f| %>
<%= f.text_field :counter, value: "1", class: "mat-input-element", autofocus: true %>
<button type="submit" class="mat-icon-button mat-button-base mat-primary add-button" title="<%= t('AddToUsed') %>">
<span class="fa fa-plus"></span>
</button>
<% end %>
</td>
<% end %>
<td class="no-wrap"><%= skill.code %></td>
<td><%= link_to skill.translated_name, skill %></td>
<td><%= link_to translation_for(skill.parent.name_translations), skill.parent %></td>
<td><%= skill.responsible.name %></td>
<td><%= skill.updated_by %></td>
<td class="text-right"><%= format_date(skill.updated_at) %></td>
</tr>
<% end %>
</tbody>
Thanks a lot for your help!
According to this form helpers guide, the syntax you used doesn't exist
form_with model: #model, action: :custom_action
So in this case, you have to specify the url parameter for form_with to make it works.
<%= form_with model: #skill, url: :add_to_cart_skill_path(#skill), method: :post, remote: false do |f| %>

Pass an array of user_params to update in rails

I am trying to bulk update users, and in so, passing an array of user information to user_params. I know(/believe) this is the issue, but I don't understand how to solve it, it keeps giving me the error of 'ArgumentError in UsersController#update_all - wrong number of arguments (1 for 0)'
I believe the question I'm asking is, how do I optionally pass an array to user_params for a bulk update, but can also send it a single user's information. Any information is appreciated to understand where I'm missing what is happening.
Thanks!
users_controller.rb
def update_all
params["user"].keys.each do |id|
#user = User.find(id.to_i)
#user.update_attributes(user_params(id))
end
redirect_to(users_path)
end
def user_params
params.require(:user).permit:first_name, :last_name, :email, :password, :password_confirmation, :role_id, :approved)
end
Form
<h1>Editing users</h1>
<%= form_for :user, :url => update_all_path, :html => { :method => :put } do %>
<table>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>E-Mail</th>
</tr>
<% #users.each do |user| %>
<%= fields_for "user[]", user do |user_fields| %>
<tr>
<td><%= user_fields.text_field :first_name %></td>
<td><%= user_fields.text_field :last_name %></td>
<td><%= user_fields.email_field :email %></td>
<td><%= user_fields.check_box :approved %></td>
</tr>
<% end %>
<% end %>
</table>
<div class="actions">
<%= submit_tag %>
</div>
<% end %>
<%= link_to 'Back', users_path %>
You're trying to call user_params with an argument, but that method (def user_params) takes no argument.
Since you've set up your form to send multiple users, params[:user] look like this:
{
"1"=>{"first_name"=>"K", "last_name"=>"P", "email"=>"k#", "approved"=>"1"},
"2"=>{"first_name"=>"A", "last_name"=>"Q", "email"=>"a#", "approved"=>"0"}
}
So the simplest thing to do is run each on that whole Hash instead of on .keys:
params["user"].each do |id, attributes|
User.find(id).update_attributes(attributes)
end
That avoids your strong parameter requirements, though, but you're going to need to do a little more work in there to handle the whole hash. After requiring :user, you need to permit fields on each value in that hash:
def bulk_user_params
users = params.require(:user).permit!
users.each do |id, attributes|
users[id] = attributes.permit(:first_name, :last_name, :email, :approved)
end
end
The permit! allows any keys in params[:user], which you need because those keys are 1, 2, etc. Then the looped permit and assignment filters the attribute hashes down to what you expect.
Once the new, safe hash is constructed, use it in update_all instead of the basic user_params:
bulk_user_params.each do |id, attributes|
User.find(id).update_attributes(attributes)
end
#user.update_attributes(user_params(id))
should be
#user.update_attributes(user_params)

update multiple records in one form using checkboxes / wrong number of arguments rails 4

I've been bumbling my way through tutorials/forums in an attempt to figure out how update multiple methods using checkboxes.
I need to be able to list my "events" and check or uncheck them if the date is available. I'd like to do this on one form. I know this is possible, but I'm finally coming here after failing to get it working.
With the code I've posted I'm getting "Wrong Number of Arguments, 2 for 1" error.
I've tried these info sources:
http://railscasts.com/episodes/52-update-through-checkboxes
http://discuss.codeschool.io/t/surviving-apis-with-rails-posting-multiple-records/4776/4
http://railscasts.com/episodes/165-edit-multiple-revised
Here's where I'm at
routes.rb
resources :events do
collection do
put :verified
post :make_events
end
end
events_controller.rb
def verified
if Event.update_all(["available", true], :id => params[:event_ids])
redirect_to step2_path(:pid => #project.id, :u => current_user.id)
else
end
end
show.html.erb
<%= form_tag verified_events_path(:pid => #project.id ), method: :put do %>
<table class="table-event-dates">
<thead>
<tr>
<th> </th>
<th> </th>
</tr>
</thead>
<tbody>
<tr><% #these_events.each do |event| %>
<td><%= check_box_tag "event_id[]", event.id, :value => event.available %></td>
<td><label> <%= event.date.strftime("%A, %b. %d %G") %></label></td>
</tr>
</tbody>
<% end %>
</table>
</div><br><!-- panel-body -->
<div class="panel-footer2">
<div class="row">
<%= submit_tag 'Verify Dates', :class => 'btn btn-green btn-lg btn-block' %>
<% end %>
Probably it should work in old-fashioned style:
Event.update_all("available = 1", ["id in (?)", params[:event_ids]])
perhaps available = true or 'true', i'm not sure. Or:
Event.update_all(["available", true], ["id in (?)", params[:event_ids]])
However, maybe you should clear params. Check that they are in correct form (1, 2, 4..).
Also you could try this:
Event.where(id: params[:event_ids]).update_all(available: true)
This question has been stale for a while but for what it's worth, the other error in the code provided that NothingToSeeHere mentions in the comment is:
In the view you have singular event_id
<%= check_box_tag "event_id[]", event.id, :value => event.available %>
In the controller you have plural event_ids
Event.update_all(["available", true], :id => params[:event_ids])
Both the params need to match and be plural. Zishe's last example for how to handle the update on the controller side is the convention for Rails 4.
<%= check_box_tag "event_ids[]", event.id, :value => event.available %>
Event.where( :id => params[:event_ids] ).update_all( :available => true )

how to pass params from radio_button_tags to controller

Here is my form that displays all the feedbacks that was not reviewed yet by admin:
I have 2 radio buttons next to each feedback to select accept or denied with values 1 or 2.
<% form_tag moderate_feedbacks_path, :method => :put do %>
<table>
<% #all_feedbacks.each do |feedback| %>
<tr>
<td><%= radio_button_tag :review_option, '1', false, :name => feedback.id %></td>
<td><%= radio_button_tag :review_option, '2', false, :name => feedback.id %></td>
<td><%= feedback.name %></td>
<td><%= feedback.email %></td>
<td><%= feedback.message %></td>
</tr>
<% end %>
</table>
<%= submit_tag 'Apply' %>
<% end -%>
what I want to do is when I click submit_tag, to update the review_option field for each selected feedback with the value of that radio_button_tag, 1 or 2
I have by now the form as you see it, works good, but I am stuck at the controller part:
def moderate_feedbacks
Feedback.update_all(["review_option = ?", ????])
redirect_to admin_feedbacks_path
end
how do I pass the params from those radio buttons to the controller.
Thank you.
p.s. html source:
<input id="review_option_1" name="3" type="radio" value="1">
<input id="review_option_2" name="3" type="radio" value="2">
name is taken from feedback.id
logs when I press the submit_tag looks like this;
Processing Admin::FeedbacksController#moderate_feedbacks (for 127.0.0.1 at 2012-10-16 15:36:20) [PUT]
Parameters: {"commit"=>"Apply", "3"=>"2", "4"=>"1"}
where 3 is the id of feedback - 2 the radio value, 4 is the id of feedback - 1 the radio value
after raise.params["feedback.is"].inspect
Parameters:
{"commit"=>"Apply",
"3"=>"1",
"4"=>"1",
"_method"=>"put"}
ok, so here is the answer:
in the feedback.rb
class Status
ACCEPTED = 1
REJECTED = 2
end
in the form:
<% form_tag moderate_feedbacks_path, :method => :put do %>
<table>
<% #all_feedbacks.each do |feedback| %>
<tr>
<td><%= radio_button_tag :review_option, Feedback::Status::ACCEPTED, false, :name => feedback.id %></td>
<td><%= radio_button_tag :review_option, Feedback::Status::REJECTED, false, :name => feedback.id %></td>
<td><%= feedback.name %></td>
<td><%= feedback.email %></td>
<td><%= feedback.message %></td>
</tr>
<% end %>
</table>
<%= submit_tag 'Apply' %>
<% end -%>
in the feedbacks_controller.rb
def moderate_feedbacks
params.each do |key, value|
if key =~ /^r(\d+)/ && !value.blank?
feedback_id = $1
Feedback.update_all(["review_option = ?", value.to_i], ["id = ?", feedback_id])
end
end
redirect_to admin_feedbacks_path
end
When I'm not sure how the data is passed in params what I like to do is raise an exception in the controller. The development exception handler outputs really great information about the contents of params. I can even put in an .inspect for my exception and see the details.
def update
raise "some string"
end
or
def update
raise params["feedback.id"].inspect
end
Also note that the first argument to radio_button_tag is the name, so you do not have to pass it in the options.

Help with rails link_to and post methods

I need help assigning students to batches.. they are in a many to many relation.
<tbody>
<% Batch.all.each do |b|%>
<tr>
<td><%= b.course.name%></td>
<td><%= b.name %></td>
<td><%= b.section_name%></td>
<td><%= link_to "Add", student_batch_students_path(#student, :batch_id=> b.id), :method=> :post%></td>
</tr>
<%end%>
</tbody>
In my controller
def create
#batch_student = BatchStudent.new(params[:batch_student])
#batch_student.save
end
My routes
resources :students do
resources :batch_students
end
resources :batches
But on my database it creates it with student_id and batch_id as null
You are updating exist batch, but not creating, so you should make PUT request to update action
<td><%= link_to "Add", student_batch_students_path(#student, :batch_id => b.id), :method=> :post %></td>
def create
#student = Student.find(params[:id])
#batch = Batch.find(params[:batch_id])
#batch_student = BatchStudent.new(:params[:batch_student])
#batch_student.student = #student
#batch_student.batch = #batch
#batch_student.save
end
The params hash doesn't contain a :batch_student hash because you are not submitting from a form. The params has should look something like {"student_id" => 1, "batch_id" => 1, "method" => "post"}.
So, modify your create action as follows:
def create
#batch_student = BatchStudent.new(params)
#batch_student.save
end
# or, a shorter version
def create
#batch_student = BatchStudent.create(params)
end
The advantage of using new is you can do a if #batch_student.save to check for errors.
I hope this helps.
The parameters and the http method should be together {:batch_id=> b.id, :method=> :post}
<%= link_to "Add", student_batch_students_path(#student), {:batch_id=> b.id, :method=> :post} %>

Resources