How do you access params[:model][:field] in Rails 4? - ruby-on-rails

From what I understand...
If you have a form_for #model, params[:model] is available when the form is submitted. Furthermore, if the form has 2 attributes attr1 and attr2, params[:model][:attr1] and params[:model][:attr2] are available when the form is submitted.
In Rails 4, you're supposed to write a method model_params that says params.require(:model).permit(:attr1, :attr2).
You'd then use model_params like so: Model.new(model_params).
However, what do you do if you only need one of the fields from the form? Like if you only needed params[:model][:attr1]?
Example:
def create
#user = User.new(user_params)
if #user.save
# need access to params[:user][:password] here
redirect_to root_url, :notice => "Signed up!"
else
render :new
end
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end

The gem responsible for this behaviour is strong_parameters. The permit() method decides what to pass on to the model based on the attributes you pass to it.
In your case, you passed it :attr1 and :attr2:
params.require(:model).permit(:attr1, :attr2)
This means the model will have attr1 and attr2 set to whatever values were passed from the form.
If you only wanted to set attr1, just remove attr2 from the permit() call.
params.require(:model).permit(:attr1)
You model will not have attr1 set, but not attr2. It's that simple.
You can even permit everything (not recommended) by calling permit! with a bang and no arguments.
You can read more about this behaviour on the gem's Github project page.
Update based on the OP's edit
If you need access to params[:user][:password] in the controller... well, you just accessed it in your example. You accessed it by typing params[:user][:password].
Nothing prevents you from accessing the params hash directly. strong_parameter's job is to prevent you from mass assigning a hash to a model, that's all.

Related

How to merge a value in a params has in ruby 1.8 and rails 2.x

I have a legacy rails application that has some security issues I am trying to fix.
In particular it is saving from a post using:
#user.update_attributes(params[:user])
I want to make sure that when saving, one of the attributes is explicitly set in the controller. There is a variable:
#company
I want to make sure the user.company_id has the value from #company variable before the call to update_attributes.
You can merge into params at the controller if you want.
user_params = params[:user].merge(:company_id => #company.id)
#user.update_attributes(user_params)
The merge will ensure any company_id in the params gets overwritten
just update the user params along with company_id by trying something like this.. it is always advisable to whitelist params by defining pvt method..
def user_params
params.permit(:user)
end
#user.update_attributes(user_params.to_h.merge!({company_id: #company.id}))
If company_id is not present in params[:user] then you can set comapany_id using before_filter in the controller as:
before_filter: set_company_id
private
def set_company_id
##Assuming you have the #user and #company objects before using
#user.company_id = #company.id
end
so after when you execute:
#user.update_attributes(params[:user])
#user object will have all the desired attributes saved...

Rails 5 redirect with strong parameters

Controller:
def foo
redirect_to home_path(params: auth_params)
end
private
def auth_params
params.require(:auth).permit(:name, :email, :photo_url, :provider, :provider_id)
end
Here are the parameters that the controller errors out on:
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"...",
"auth"=>
{"name"=>"...",
"email"=>"...",
"photo_url"=>"...",
"provider"=>"...",
"provider_id"=>"..."}
}
With this error: unable to convert unpermitted parameters to hash
I permit all the params (with the exception of the rails default params (i.e. utf8, authenticity_token, so why is this error popping up?
EDIT:
Neither of the following suggestions are working either:
redirect_to home_path(auth_params)
redirect_to home_path, params: auth_params
A little more info on how I get to the controller action might help:
I have (in the same controller) an action bar that has a form_tag in the view that goes to foo_path. I've tried submitting the form via jQuery or a submit button, it doesn't change the result. As soon as I hit the auth_params method I get the error.
This means it is erroring out on the auth_params method rather than the redirect itself
I have get this error too when I have upgrade to Rails 5.1.4. I fixed with this
redirect_to(home_path, params: params.require(:auth).permit(:name, :email, :photo_url, :provider, :provider_id))
I hope this help you.
After I restarted the development server AND cleared all the cookies placed by rails (most notably the app-name session cookie), the normal strong parameter syntax (i.e. params.require().permit()) works fine.
The home_path and other url_for related methods are expecting a Hash object instead of an ActionController::Parameters object (which is what the params method in controllers returns) so you need to convert it to a hash first, like so:
redirect_to home_path(auth_params.to_h)
You´re doing it very wrong. If you are creating a resource then you should reply to validation errors by rendering a view - not redirecting.
The response you´re sending the user is the result of performing a non-idempotent action.
This might seem like nitpicking but outing the users email & chosen password in the GET parameters is pretty reckless.
If you are embedding a form in your homepage its pretty simple to just create a auths/new.html.erb view which just shows the form and lets the user correct the misstakes:
class AuthsController
def create
#auth = Auth.new(auth_params)
if #auth.save
redirect_to some_path, success: "Your account was created"
else
render :new
end
end
private
def auth_params
params.require(:auth).permit(:name, :email, :photo_url, :provider, :provider_id)
end
end
If the form has to be seamlessly included in the home page then you need to render that view or use ajax to perform it in place.

Rails create action: Forbidden Attributes Error

I am new to rails and am in the process of entering in information in a form and saving it to a database. I am following a tutorial which may be out of date. I am getting an error on the second line. Am I passing the wrong parameter?
def create
#student = Student.new(params[:student])
if #student.save
redirect_to new_student_path
end
end
I expect the problem is that you need to process the student parameters before passing them to Student.new, so as not to fall foul of the strong parameters system.
Try this:
def create
#student = Student.new(student_parameters)
if #student.save
redirect_to new_student_path
end
end
private
def student_parameters
params.require(:student).permit(:name, :age)
end
replace :name, :age with the list of attributes you want to white list (allow through)
More information about the mass assignment vulnerability that strong parameters mitigates can be found here.

How to remove a field from params[:something]

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>

Conditional value for ActiveRecord create method only

I have a form where I have an administrator creating new users. The form uses the User model I created (login, password, first_name, etc...). For the last field on the form, I want to have a checkbox that doesn't need to be stored as part of the User record, but it is needed for the controller. This will control if the newly created user will receive a welcome email or not. This is in Rails 3.0.3.
def create
#user = User.new(params[:user])
if #user.save
if #user.send_welcome_email
UserMailer.welcome_email(#user).deliver
end
redirect_to(admin_users_url, :notice => "User #{#user.name} was successfully created.")
else
render :action => "new"
end
end
In my view (haml) I am trying to access it like this:
%p
Send Welcome Email?
= f.check_box :send_welcome_email
I tried to make this an attr_accessible: :send_welcome_email but the controller does not recognize it. I get an
undefined method 'send_welcome_email' for #<User:0x00000100d080a8>;
I would like it to look like this:
What is the best way to get this working?
What you want is not attr_accessible, but attr_accessor. That's it.
However, your code will look nicer if you move the email sending code to an observer.
Since you're not saving it on the user, you can use check_box_tag instead of f.check_box and access it with params[:send_welcome_email]. Although even the way you have it, I think you could access it as params[:user][:send_welcome_email].
As an alternative to attr_accessor, you can always remove it from the parameters first:
def create
send_welcome_email = params[:user].delete(:send_welcome_email)
#user = User.new(params[:user])
if #user.save
UserMailer.welcome_email(#user).deliver if send_welcome_email
redirect_to(admin_users_url, :notice => "User #{#user.name} was successfully created.")
else
render :action => "new"
end
end
You may have to make sure that the parameter is successfully transformed into a boolean; otherwise the condition will always be true (0 is true in Ruby).

Resources