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)
Related
I have manager remark model that takes input as a remark and decision value and saves it with the project site ID. I have a project site model that takes input as name, date, and file and stores it. Many remarks have a many to one relation with project site ID, and the project site belongs to the manager remark. I want to access the decision attribute boolean value in project site index form, but I am unable to access that boolean value in the index page of the project site. Here is my code of project site and manager remarks model, view and controller-
project site index.html.erb
<table>
<thead>
<tr>
<th>Name</th>
<th>Date</th>
<th>Attendance</th>
<th>Status</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #project_sites.each do |project_site| %>
<tr>
<td><%= project_site.name.titleize %></td>
<td><%= project_site.date %></td>
<td><%= link_to ' View attendance', project_site.file, :class => "fi-page-export-csv" %></td>
<td><%= "here i want to access manager remark decision value" %></td>
<td><%= link_to 'Remark ', project_site %><span>(<%= project_site.manager_remarks.size %>)</span></td>
<td><%= link_to 'Edit', edit_project_site_path(project_site) %></td>
<td><%= link_to 'Destroy', project_site, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
project site controller
def index
#project_sites = ProjectSite.all.order("created_at DESC")
#manager_remark = ManagerRemark.joins(:project_site).where(:project_sites => { :user_id => #user.id })
end
# GET /project_sites/1
# GET /project_sites/1.json
def show
#manager_remark = ManagerRemark.new
#manager_remark.project_site_id = #project_site.id
end
# GET /project_sites/new
def new
#project_site = ProjectSite.new
end
def project_site_params
params.require(:project_site).permit(:name, :date, :file)
end
manager_remark controller
class ManagerRemarksController < ApplicationController
def create
#manager_remark = ManagerRemark.new(remark_params)
#manager_remark.project_site_id = params[:project_site_id]
#manager_remark.save
redirect_to project_site_path(#manager_remark.project_site)
end
def remark_params
params.require(:manager_remark).permit(:remark, :decision)
end
end
manager_remark view form
<%= form_for [ #project_site, #manager_remark ] do |f| %>
<div class="row">
<div class="medium-6 columns">
<%= f.radio_button :decision, true %>
<%= f.label :approve %>
<%= f.radio_button :decision, false %>
<%= f.label :reject %>
</div>
<br>
<br>
<div class="medium-6 cloumns">
<%= f.label :remark %><br/>
<%= f.text_area :remark %>
</div>
</div>
<div>
<%= f.submit 'Submit', :class => 'button primary' %>
</div>
<% end %>
routes.rb
Rails.application.routes.draw do
root to: 'home#index'
devise_for :users
resources :project_sites do
resources :manager_remarks
end
get '/project_manager_level_two' => 'project_manager_level_two#index'
get '/project_managers' => 'project_managers#index'
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
If I understand correctly, you have a ProjectSite that contains a ManagerRemark with a decision, right? If that's the case, the simple answer is:
<%= project_site.ManagerRemark.decision %>
If you are saying that each ProjectSite has many ManagerRemarks, you'll want to place the above inside a loop, like so:
<% project_site.manager_remarks.each do |manager_remark| %>
<%= manager_remark.decision %><br/>
<% end %>
This assumes that your models are correctly configured to recognize these relationships. The above may also be optimized by adding an include clause to your fetch inside the controller and there's no need to fetch the ManagerRemark objects separately. Therefore, you'd probably want something like:
def index
#project_sites = ProjectSite.all.includes( :manager_remark ).order("created_at DESC")
end
I wan to do a generic grid, that could be used in my app to show data from different Models. As I am a newbie in RoR - please let me know if my "vision" is ok, or i should change the way i think about this problem:
Controller:
class Admin::UsersController < ApplicationController
before_action :authenticate_user!
authorize_resource
def index
#current_scope = params[:role]
#users = User.select(:id, :email, :role, :last_sign_in_at).role(#current_scope)
end
end
User Model:
scope :role, -> (role) {where('role = ?', role) if role.present?}
Index view:
<%= render '/layouts/shared/grid', object: #users%>
Grid partial:
<% object.column_names.each do |column_name| %>
<th><%= column_name %></th>
<% end %>
My problem is that i can show always ALL columns (that is what ActiveRecord::Relation returns), not only columns for parameters selected in my query. I see few potential solutions - which one would be the best?
usage of ActiveRecord::Base.connection.exec_query - if i do so, i could use then .columns method that returns what i need. But i am not sure if i like it...
usage of search_params instead of query result to iterate
Any other suggestion?
You can use values method. It's not very documented but it returns parsed sql query.
For this query:
User.select(:id, :email, :role,:last_sign_in_at).role(#current_scope).values
It returns something like this:
{:select=>[:id, :email, :role, :last_sign_in_at], :where=>#<ActiveRecord::Relation::WhereClause:0x007fe0d3b29bf0 #predicates=["role = admin"], #binds=[]>}
If a query will not use select when values will not return select result too. In this case, you need to use column_names.
Too I can recommend you wrap table code to decorator. This is raw example:
class TableDecorator
def initialize(scope)
#scope = scope
end
def header_columns
scope.values[:select] || #scope.column_names
end
def columns
#scope
end
end
# controller
#table = TableDecorator.new(#users)
# index view
<%= render '/layouts/shared/grid', locals: {table: #table} %>
# grid partial
<table>
<thead>
<tr>
<% table.header_columns.each do |column_name| %>
<th><%= column_name %></th>
<% end %>
</tr>
</thead>
<tbody>
<% table.columns.each do |item| %>
<tr>
<% table.header_columns.each do |column_name| %>
<td><%= item[column_name] %></td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
I am building a timesheet project. i want to insert multiple records depending on the count of projects allocated to employes.
the problem is how to insert multiple records into in a table using forms
For example is the image i was trying to do:
code for form is
<%= form_for(:timesheets, :url => {:action => 'create',:employee_id => #pro.id}) do |d| %>
<% if !#project.nil? %>
<% #project.each do |page| %>
<tr>
<%= d.hidden_field("employee_id" ,:value => #pro.id) %>
<%= d.hidden_field("project_id" ,:value => page.id) %>
<% if !page.employee_id.blank? %>
<td><%= page.prog_name %></td>
<td><%= d.text_field("IN",:class => "qty1") %></td>
<td><%= d.text_field("comments") %></td>
<% end %>
</tr>
<% end %>
<% end %>
<tr>
<td>Total hours</td>
<td colspan="2"><%= text_field_tag("total")%></td>
</tr>
<tr border="0">
<td><%= submit_tag("Submit") %></td>
<td colspan="2" border="0"></td>
</tr>
<% end %>
Your question is really vague, but I think I get what you're asking. I had to build a similar form recently, and used a form object with a row class, with an instance of the row class being initialized for every record that needed to be added.
See this for an introduction to form objects if you're not familiar already: http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
class TimesheetForm
# include ActiveModel stuff here
# attr_reader :projects, ...
# validations for form as a whole, if desired
def initalize(employee)
#employee = employee
#projects = employee.projects
end
def submit(params)
# extract params and create new instances of TimesheetForProject for each set of timesheet params
# run validations on all #timesheet_rows
# persist if all validations pass
end
class TimesheetForProject
# validations for each timesheet record
def initialize(project)
#project = project
end
# timesheet-specific form logic
end
end
This doesn't contain a lot of details you need, but it's a rough conceptual outline of a pattern I've seen and used to solve this type of problem.
I've been getting this error for a couple days and I'm totally blocked. I tried redoing the model (I'm following the rails starting guide) and just not getting anywhere.
First argument in form cannot contain nil or be empty
I have time_delta as a nested class of stock im trying to create a form to view and create new time_deltas on a stock's show and I keep getting the above error.
Heres my time_delta controller:
class TimeDeltasController < ApplicationController
def new
#stock = Stock.find(params[:stock_id])
#time_delta = #stock.time_deltas.build
respond_with(#time_delta)
end
def create
#stock = Stock.find(params[:stock_id])
#time_delta = #stock.time_deltas.build(params[:stock])
#time_delta.save
end
end
Heres my view for the specific stock
<h1> Stock </h1>
<table>
<tr>
<th>Stock</th>
<th>Hashtag</th>
</tr>
<tr>
<td><%= #stock.name %></td>
<td><%= #stock.hashtag %></td>
</tr>
</table>
<h2>Deltas: </h2>
<table>
<tr>
<th>Stock</th>
<th>Hashtag</th>
</tr>
<% #stock.deltas.each do |delta| %>
<tr>
<td><%= #delta.start %></td>
<td><%= #delta.length %></td>
</tr>
<% end %>
</table>
<h2>Add a TimeDelta:</h2>
<%= form_for([#stock,#time_delta]) do |f| %>
<p>
<%= f.label :start %><br>
<%= f.text_field :start %>
</p>
<p>
<%= f.label :length %><br>
<%= f.text_area :length %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<%= link_to 'Back', stocks_path%>
<%= link_to 'Edit', edit_stock_path(#stock)%>
Heres how I added the route in my routes.rb:
resources :stocks do
resources :time_deltas
end
Any information would be greatly appreciated, I'm really stuck.
EDIT: Stocks controller stuff
def show
#time_delta = #stock.time_deltas.build
#stock = find_stock
end
private
def find_stock
return Stock.find(params[:id])
end
You are trying to access #time_delta variable in your stocks/show view, but it is not set. Add the following line to StocksController#show action.
#time_delta = #stock.time_deltas.build
EDIT:
Also there is a problem with the naming of your TimeDelta model, because in Ruby 'delta' is plural of 'deltum'. To adhere to Rails conventions, change the the model name to TimeDeltum or alternatively tell Rails to use 'deltas' as the plural form of 'delta'. You can learn how to do it here.
The error basically means you've not set the variables for use in form_for
As you're a beginner, let me explain how it works:
form_for is basically an instance method (helper) which takes ActiveRecord objects, and outputs them into an HTML form. Your definitions of elements (f.____field) are for the method to determine which HTML to output
Like all methods, form_for has arguments/parameters which it relies on to help the method run correctly. The parameters for form_for include a correctly formatted ActiveRecord object, which is why you have to call Model.new each time you want to use it
Your error means the method cannot process the variables (objects) you've sent, either from lack of data (empty) or non-existence (nil). To fix this, as #vee has mentioned, you need to make sure your form_for is receiving the correct data
I would try this:
class TimeDeltasController < ApplicationController
def new
#stock = Stock.find(params[:stock_id])
#time_delta = #stock.time_deltas.build
respond_with(#stock, #time_delta)
end
def create
#stock = Stock.find(params[:stock_id])
#time_delta = #stock.time_deltas.build(params[:stock])
#time_delta.save
end
end
You should read up on respond_with to see how it works
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