When I try to create a user, Rails returns a ParameterMissing error:
ActionController::ParameterMissing in UserController#create
param is missing or the value is empty: user
My Controller
class UserController < ApplicationController
def create
#user = User.new(user_params)
render json: #user
end
private
def user_params
params.require(:user).permit(:fame, :lame, :mobile)
end
end
My User Model
class User < ActiveRecord::Base
self.table_name = "user"
end
What am I doing wrong?
Check your logs for the params that are being sent to your controller. Most likely, the params hash being sent by your view doesn't include the :user, key. To fix, you'll need to make sure your form_for is properly namespaced with a User object:
form_for #user do |f|
# ...
end
You can also use the as key to explicitly set the :user key in your params.
form_for #object, as: :user, method: :post do |f|
# ...
end
Update
Since the questioner was using postman to send data, the data sent to the server should be properly formatted like so:
user[firstName]
Thanks #Fabio and #Anthony
When you asked about the form, I actually realized that the parameter I sending with postman was actually incorrect as they should be like
user[firstName]
Updated
It actually deepns upon you how you send the params.
I send as
user[firstname] So I get like params[:user][:firstName]
If I send like firstname So this will be params[:firstName]
Related
I have an issue with an automatically generated token. In a model, I generate the token automatically using:
class User < ApplicationRecord
before_create :generate_confirm_token
def generate_confirm_token
self.confirm_token = generate_token
end
def generate_token
loop do
token = SecureRandom.hex(10)
break token unless User.where(confirm_token: token).exists?
end
end
After creating of user, the token is generated correctly, but the issue is in a controller:
class Companies::StudentsController < ApplicationController
def create
#company = Company.find(params[:company_id])
#student = #company.students.create(student_params)
raise #student.inspect
if #student.save
StudentMailer.with(student: #student).welcome_email.deliver_now
redirect_to company_students_path
else
render :new
end
end
student contains confirm_token BUT in params the confirm token is empty.
I need the token in params because in the mailer I use Find_by(params[:confirm_token]).
Here is how I use a confirm_token in my view. I assume I need the confirm_token in params so I have to have it in a view also:
<%= f.hidden_field :confirm_token %>
The process which is described above is OK.
The issue was in mailer.
student should be in mailer created like this:
#student = params[:student]
but I did it like this:
#student = Student.find_by(confirm_token: :confirm_token)
Which is not correct according to the mailer documentation:
Any key value pair passed to with just becomes the params for the
mailer action. So with(user: #user, account: #user.account) makes
params[:user] and params[:account] available in the mailer action.
Just like controllers have params.
I'm doing simple Reddit like site. I'm trying to add button to reporting posts. I create report model, using button_to i try to post data to report controller to create it but i received NoMethodError in ReportsController#create undefined method merge' for "post_id":String
model/report.rb
class Report < ApplicationRecord
belongs_to :reporting_user, class_name: 'Author'
has_one :post
end
report_controller.rb
class ReportsController < ApplicationController
def create
report = Report.new(report_params)
flash[:notice] = if report.save
'Raported'
else
report.errors.full_messages.join('. ')
end
end
def report_params
params.require(:post).merge(reporting_user: current_author.id)
end
end
and button in view
= button_to "Report", reports_path, method: :post, params: {post: post}
What cause that problem?
edit:
params
=> <ActionController::Parameters {"authenticity_token"=>"sX0DQfM0rp97q8i16LGZfXPoSJNx15Hk4mmP35uFVh52bzVa30ei/Bxk/Bm40gnFmd2NvFEqj+Wze8ted66kig==", "post"=>"1", "controller"=>"reports", "action"=>"create"} permitted: false>
To start with you want to use belongs_to and not has_one.
class Report < ApplicationRecord
belongs_to :reporting_user, class_name: 'Author'
belongs_to :post
end
This correctly places the post_id foreign key column on reports. Using has_one places the fk column on posts which won't work.
And a generally superior solution would be to make reports a nested resource:
# /config/routes.rb
resources :posts do
resources :reports, only: [:create]
end
# app/controller/reports_controller.rb
class ReportsController
before_action :set_post
# POST /posts/:post_id/reports
def create
#report = #post.reports.new(reporting_user: current_author)
if #report.save
flash[:notice] = 'Reported'
else
flash[:notice] = report.errors.full_messages.join('. ')
end
redirect_to #post
end
private
def set_post
#post = Post.find(params[:post_id])
end
end
This lets you simplify the button to just:
= button_to "Report", post_reports_path(post), method: :post
Since the post_id is part of the path we don't need to send any additional params.
If you do want to let the user pass additional info through a form in the future a better way to create/update resources with params and session data is by passing a block:
#report = #post.reports.new(report_params) do |r|
r.reporting_user = current_user
end
ActionController::Parameters#require returns the value of the required key in the params. Usually this would be an object passed back from a form. In this example require would return {name: "Francesco", age: 22, role: "admin"} and merge would work.
Your view is sending back parameters that Rails is formatting into {post: 'string'}. We would need to see your view code to determine what exactly needs to change.
Update: From the new code posted we can see that the parameter sent back is "post"=>"1". Normally we would be expecting post: {id: 1, ...}.
Update: The button in the view would need the params key updated to something ala params: {post: {id: post.id}} EDIT: I agree that params: {report: { post_id: post}} is a better format.
The problem seems to be in report_params. When you call params.require(:post), it fetches :post from params -> the result is string. And you are calling merge on this string.
I'd recommend change in view:
= button_to "Report", reports_path, method: :post, params: { report: { post_id: post} }
then in controller:
def report_params
params.require(:report).permit(:post_id).merge(reporting_user_id: current_author.id)
end
Note, that I changed also the naming according to conventions: model_id for id of the model, model or model itself.
I have a custom field phone for a User that is not stored in the database, since I'm using it for other purposes. Each field is in a different partial and can be updated on it's own via AJAX. This is how I handle this in the UsersController:
app/controllers/users_controller.rb
def update
#user = ...
if #user.update_attributes(params[:user])
respond_to do |format|
format.json { render json: {html: render_to_string partial: 'edit_field', locals: { user: #user }} }
end
end
end
I have the submitted phone param available in a custom User model attribute custom_attrs:
app/models/user.rb
include UserConcern
...
attr_accessor :custom_attrs
...
# I've shorten this method, so that you can get the idea, might not work in reality
def update_attributes
self.custom_attrs[:phone] = self.phone
end
I can't add that to the #user object before I return the response in the controller, I get ActiveModel::MissingAttributeError: can't write unknown attribute 'phone'.
In my view the phone field value is set by a method in the UserConcern:
app/models/concerns/user_concern.rb
def phone
self.phone = fetch_phone(user) # this method returns the users' phone value
end
When I update the phone and re-render the partial, it still has the same value, because the #user object doesn't have phone, since it's not a field in the model.
I also can't do send("phone=", value), because there's no such method in the controller (results in NoMethodError: undefined method 'phone_number_mobile=' for #<UsersController:)
So - how do set the value for phone to the newly updated object #user, either by the #user.custom_attrs or the UserConcern?
There is a simpler way.
app/models/user.rb
...
attr_accessor :phone
...
You can then access user.phone
My Model
class User < ActiveRecord::Base
end
My Controller
class UsersController < ApplicationController
...
end
When I post addition field to post action such as { "xx": 7 ... }
{"user_email"=>"scott#student", "user_pw"=>"123456", "xx"=>7, "controller"=>"users", "action"=>"create", "user"=>{"user_pw"=>"123456", "user_email"=>"scott#student"}}
I can not found the "xx=>7" in "user" attribute. How can I move it to param["user"]["xx"].
Here is my code
due to "xx" is not UserModel attributes so, I guess that UserController using fields of UserModel to filter all parameters automatically, if the controllerName is mapped with modelName with same naming rule. So finnally I missing "xx" in user node inside.
class User < ActiveRecord::Base
# include fields: uid, user_name, user_mobile.
end
class UsersController < ApplicationController
def user_params
params.fetch(:user).extract!(:uid).permit!
end
def create
user = User.new(user_params)
user.save!
render :nothing => true, :status => 202
end
end
I can set a trick wrap_parameter setting in controller
wrap_parameters :user, include: User.column_names << 'xx'
or
wrap_parameters true
# but user node will be "true" => {"xxx": "test", "user_name": ... }, acception all field even if it's not UserModel attributes.
Finally, I want that the wrap_parameters 'user' without setup include all columns which client send to server. I don't want to setup like include: [*column_names].
It looks like you are currently relying on parameter wrapping, in particular you're letting it work in its most automatic mode. In this scenario if there is a param named foo submitted to UsersController and if the model User has a field of that name then rails copies it into params[:user].
You can change this behaviour in two ways: you could redefine attribute_names on your model to return this extra column. I'm not sure what else uses that method though - may have unintended consequences elsewhere.
The less disruptive change would be to put
wrap_parameters User, include: [:user_pw, :user_email, :xx]
At the top of the controller, to explicitly tell action controller which params should be treated in this way.
In your form partial/view you have to put the hidden attribute or however you set the xx value inside the form.
<%= form_for #user, do |f| %>
......
<%= f.hidden_field :xx, value: "7" %>
<% end %>
This way it will be under "user".
My registration form, which is a form for the Users model, takes a string value for company. However, I have just made a change such that users belongs_to companies. Therefore, I need to pass an object of Company to the Users model.
I want to use the string value from the form to obtain the an object of Company:
#user.company = Company.find_by_name(params[:company])
I believe the above works, however the form is passing the :company (which is string) into the model when I call:
#user = User.new(params[:user])
Therefore, I want to know (and cannot find how) to remove the :company param before passing it to the User model.
Rails 4/5 - edited answer
(see comments)
Since this question was written newer versions of Rails have added the extract! and except eg:
new_params = params.except[the one I wish to remove]
This is a safer way to 'grab' all the params you need into a copy WITHOUT destroying the original passed in params (which is NOT a good thing to do as it will make debugging and maintenance of your code very hard over time).
Or you could just pass directly without copying eg:
#person.update(params[:person].except(:admin))
The extract! (has the ! bang operator) will modify the original so use with more care!
Original Answer
You can remove a key/value pair from a Hash using Hash#delete:
params.delete :company
If it's contained in params[:user], then you'd use this:
params[:user].delete :company
You should probably be using hash.except
class MyController < ApplicationController
def explore_session_params
params[:explore_session].except(:account_id, :creator)
end
end
It accomplishes 2 things: allows you to exclude more than 1 key at a time, and doesn't modify the original hash.
The correct way to achieve this is using strong_params
class UsersController < ApplicationController
def create
#user = User.new(user_params)
end
private
def user_params
params.require(:user).permit(:name, :age)
end
end
This way you have more control over which params should be passed to model
respond_to do |format|
if params[:company].present?
format.html { redirect_to(:controller => :shopping, :action => :index) }
else
format.html
end
end
this will remove params from the url
Rails 5+: Use the handy extract! method with strong params!
The extract! method removes the desired variable from the Parameters object (docs) and returns a new ActionController::Parameters object. This allows you to handle params properly (i.e. with strong params) and deal with the extracted variable separately.
Example:
# Request { user: { company: 'a', name: 'b', age: 100 } }
# this line removes company from params
company = params.require(:user).extract!(:company)
# note: the value of the new variable `company` is { company: 'a' }
# since extract! returns an instance of ActionController::Parameters
# we permit :name and :age, and receive no errors or warnings since
# company has been removed from params
params.require(:user).permit(:name, :age)
# if desired, we could use the extracted variable as the question indicates
#company = Company.find_by_name(company.require(:company))
Full example in controller
Of course, we could wrap this up in a handy method in our controller:
class UsersController < ApplicationController
before_action :set_user, only: [:create]
def create
# ...
#user.save
end
def set_user
company = params.require(:user).extract!(:company)
#user = User.new(params.require(:user).permit(:name, :age))
#user.company = Company.find_by_name(company.require(:company))
end
end
To be possible to delete you can do a memo:
def parameters
#parameters ||= params.require(:root).permit(:foo, :bar)
end
Now you can do:
parameteres.delete(:bar)
parameters
=> <ActionController::Parameters {"foo" => "foo"} permitted: true>