Rails Button_to not passing model param - ruby-on-rails

I have the following in my products index page:
<%= button_to "Add", user_order_orderitems_path(user_id: current_user.id, item_id: x.id, order_id: current_user.group.current_order.id), class: "btn btn-mini" %>
Which I can see from the logs is being picked up by my Orderitems#create action in my controller ok. This looks like:
def create
#orderitem = Orderitem.new(orderitem_params)
if #orderitem.save
redirect_to items_path
else
redirect_to items_path
end
end
private
def orderitem_params
params.require(:orderitem).permit(:user_id, :order_id, :item_id)
end
end
The params specified in the button_to call are being created and are showing up in the logs as:
Started POST "/users/1/orders/1/orderitems?item_id=2264" for 127.0.0.1 at 2013-07-03 22:45:24 +0100
Processing by OrderitemsController#create as HTML
Parameters: {"authenticity_token"=>"blah=", "item_id"=>"2264", "user_id"=>"1", "order_id"=>"1"}
Fnally - the problem - my strong_params method can't process these params as the three params I care about are not nested in a hash with 'Orderitem's as a key. I would expect, for my create action to work, I need something like:
Parameters: {"authenticity_token"=>"blah=", "orderitems"=>{"item_id"=>"2264", "user_id"=>"1", "order_id"=>"1"}}
but I can't for the life of me work out how, with button_to I am able to do this - I have tried a form_for, but this also failed to work. Banging my head on a brick wall for a couple of days on this one...So, how can post my three ids to my OrderItemsController create action from an index view for Products but bypassing any form_for or new actions? Is it possible?
Please let me know if I am approaching this scenario (adding an item to a basket) in completely the wrong way.

This way you can treat a standard hash as one supported by strong parameters
raw_parameters = {"authenticity_token"=>"blah=", "item_id"=>"2264", "user_id"=>"1", "order_id"=>"1"}
parameters = ActionController::Parameters.new(raw_parameters)
parameters.permit(:user_id, :order_id, :item_id)

Related

Rails "param is missing or the value is empty" error

I have this link_to helper passing an :email_sequence instance and an extra :set_active params.
I then try to update the :email_sequence instance in the controller using strong params but I'm getting an error saying:
param is missing or the value is empty: email_sequence
link_to:
<%= link_to "Activate", admin_email_sequence_path(base_email.email_sequence, set_active: :true), method: :patch %>
Controller:
class Admin::EmailSequencesController < AdminController
before_action :set_email_sequence
def update
if #email_sequence.update(active: email_sequence_params[:set_active])
flash[:success] = "Sequence updated succesfully"
redirect_to_forwarder_or(params[:forwarding_uri], admin_account_emails_path)
end
end
private
def set_email_sequence
#email_sequence = current_account.email_sequences.find(params[:id])
end
def email_sequence_params
params.require(:email_sequence).permit(:set_active)
end
end
This is what gets sent in the params:
{"_method"=>"patch", "authenticity_token"=>"[FILTERED]", "set_active"=>"false", "id"=>"1"}
Can anybody tell me what am I doing wrong?
By params.require(:email_sequence).permit(:set_active) you expect parameters to be { email_sequence: {set_active: "ANY SCALAR VALUE HERE"} } but you pass only set_active you can fix it by permitting the only one parameter
params.permit(:set_active)
You don't need strong parameters here in the first place. Contrary to popular belief strong parameters does not magically sanitize the parameters. It just prevent mass assignment vulnerabilities by requiring that you whitelist the parameters when passing a hash of parameters. Since you are only using a single parameter there is no mass assignment vulnerability:
class Admin::EmailSequencesController < AdminController
before_action :set_email_sequence
def update
if #email_sequence.update(active: params[:set_active])
flash[:success] = "Sequence updated succesfully"
redirect_to_forwarder_or(params[:forwarding_uri], admin_account_emails_path)
else
# provide an error response!
end
end
private
def set_email_sequence
#email_sequence = current_account.email_sequences.find(params[:id])
end
end
If you later want to use multiple parameters nested in a hash the use of link_to is pretty questionable even if it can be done.
<%= link_to "Activate",
admin_email_sequence_path(
base_email.email_sequence,
"email_sequence[set_active]" => true,
"email_sequence[foo]" => "bar"
),
method: :patch
%>
Use button_to or form_with/form_for to create a form element and style the button to look the way you want instead as this places the parameters in request body instead of the query string.

Parameter ID changes with custom controller

When the form_for is as so:
<%= form_for #reservation, remote: true do |f| %>
I get the following params:
Parameters: {"utf8"=>"✓", "reservation"=>{"party_size"=>"1",
"persons_attributes"=>{"0"=>{"first_name"=>"Big Name",
"last_name"=>"test", "meal_id"=>"1", "id"=>"24"},
"1"=>{"first_name"=>"", "last_name"=>"", "meal_id"=>"1"}},
"address"=>"test", "city"=>"Test", "state"=>"te", "zip"=>"te"},
"commit"=>"submit", "id"=>"24"}
But when I put a custom controller onto the form_for, as so
<%= form_for #reservation, url: {controller: 'static_pages'}, remote: true do |f| %>
I get the following params:
Parameters: {"utf8"=>"✓", "reservation"=>{"party_size"=>"1",
"persons_attributes"=>{"0"=>{"first_name"=>"Big Name",
"last_name"=>"test", "meal_id"=>"1", "id"=>"24"},
"1"=>{"first_name"=>"", "last_name"=>"", "meal_id"=>"1"}},
"address"=>"test", "city"=>"Test", "state"=>"te", "zip"=>"te"},
"commit"=>"submit", "id"=>"index"}
How do I ensure that the params[:id] remains the same? All I need is the standard form_for with a different controller called (the same for is used for both new\ create and edit\ update).
'form_for' with an active record object tries to identify the URL from the object. If it has an ID, it uses the update route, otherwise it uses the create route. Initially it identifies it as something like
/reservation/1
But when you explicitly pass something as the URL it will override it. Now our URL looks like
/reservation/index
I recommend looking into the 'form_for' documentation, but what could really solve your problem would be to explicitly pass the whole URL, instead of just the controller. That way you could get the ID in the right place
In the form you can place the following line of code which will pass the :id to the controller in the POST request.
<%= hidden_field_tag(:id, params[:id]) %>
You might have to add a method to convert it to an integer within the controller.
params[:id].to_i
Depending on how you have your db setup.

I want to use one controller and html.erb files for my dynamic table. How I will do it in Ruby On Rails?

I stored all the tablename I've created to Menu table. And every time I add the table in Menu, it will automatically create a link under Menu list
see below.
I want each table in Menu to have a Listing, New, Edit, and Delete.
see below.
I have a controller prj_menus_controller, I will just pass the id of the table from Menu table.
here is the code for index and new in my controller.
Class PrjMenusController < ApplicationController
def index
#prj_menus = Menu.find(params[:id]).tablename.singularize.classify.constantize.all
end
def new
#prj_menu = Menu.find(params[:id]).tablename.singularize.classify.constantize.new
end
def create
#prj_menu = Menu.find(params[:id]).tablename.singularize.classify.constantize.new(prj_menu_params)
if #prj_menu.save
redirect_to :action => 'index'
else
render :new
end
end
private
def prj_menu_params
params.require("HERE IS MY PROBLEM").permit(:name)
end
end
and in my
new.html.erb
<%= simple_form_for (#prj_menu),:url => prj_menus_path, :method => :post do |f| %>
<%= f.input :name %>
<%= f.submit 'Save', class: 'btn btn-primary' %>
<%= link_to "Cancel", :back, {:class=>"btn btn-default"} %>
<% end %>
I can get the list in my index.html.erb, it is working. My problem is that I don't know how to get all params when I click the submit in new.html.erb. I got this hash
{"sample1_table"=>{"name"=>"test 6"}, "commit"=>"Save","controller"=>"prj_menus", "action"=>"create"}
It is correct but I don't know what to put in my controller. I tried this params.require(["#{#prj_menu}"]).permit(:name), it creates new record but params[:name] does not save.
I am still a noob to Ruby On Rails and I don't know what to search for this.
I think you are mostly confused on what parameter whitelisting does and how parameters are passed from the form to the controller.
I does not really matter if the name of the form hash matches the name of the database table. It just does in most cases since that makes the most sense. It's simply representative of the REST interface of your app.
Let's say you have a action which creates Pets:
POST /pets
And in our form we have a bunch of inputs like so:
<input name="pet[name]">
Rails will map create a params[:pet] hash { name: 'Spot' }. But we want to save the pets as an Animal.
class PetsController < ApplicationController
def new
#pet = Animal.new()
end
def create
#pet = Animal.new(pet_params)
if #pet.save
# ...
end
def pet_params
params.require(:pet).permit(:name)
end
end
Animal does not care what the params key is, it just gets a hash. But we also need to tell simple_form what parameter key we want to use since it looks at the model_name attribute.
simple_form_for(#pet, as: :pet)
Gives us pet[name] instead of animal[name].
I don't get why you are so adamant about making things so difficult for yourself though unless you are creating a database administration tool in the vein of PHP_MyAdmin. And even that case you don't even want to be altering the schema of the app database at runtime.
You are going to run into huge problems when it comes to creating effective queries for getting all the menus.

Losing parameters in production?

Or, that is what seems to be happening. I'm getting errors in my production environment that i can't recreate in development. Basically, i'm trying to associate a Booker to a Booking. If the Booker doesn't exist I'd like to invite him/her.
# GET /bookings/new
def new
#booking = Booking.new
authorize #booking
#booking.venue = Venue.new
#booker = User.new
end
Relevant create code;
def create
#booking = Booking.new(booking_params)
authorize #booking
booker_found = set_booker
And the set_booker private method
def set_booker
logger.info "booker: #{booking_params.inspect}"
# set existing user as booker or prepare inviting a new user
booker_found = false
#booker = User.find_by_email(booking_params[:user][:email])
etc.
The last line is where I get errors in production, because booking_params[:user] does not exist. I tried resetting my database in the development ENV and the code works fine. Yet in production I will always get NoMethodError (undefined method '[]' for nil:NilClass)
This is my relevant form code;
<%= simple_form_for(#booking, :wrapper => :bootstrap3 ) do |f| %>
<%= f.simple_fields_for #booker do |user_form| %>
<%= user_form.input :email, label: "Booker e-mail" %>
<% end %>
This is what logger shows in development;
Parameters: {"utf8"=>"✓", "authenticity_token"=>"lala", "booking"=>{"name"=>"Mah Booking", "date"=>"02-10-2014", "venue_name"=>"Meeeeeeeeeeee", "venue_id"=>"", "user"=>{"email"=>"booker#boekkoek.nl"}, "artist_fee"=>"0.00"}, "commit"=>"Create Booking", "locale"=>"en"}
booker: {"date"=>"02-10-2014", "name"=>"Mah Booking", "artist_fee"=>"0.00", "venue_id"=>"", "venue_name"=>"Meeeeeeeeeeee", "user"=>{"email"=>"booker#boekkoek.nl"}}
And this is from my production.log
Parameters: {"utf8"=>"✓", "authenticity_token"=>"yada", "booking"=>{"name"=>"tttt", "date"=>"02-10-2014", "venue_name"=>"meee", "venue_id"=>"", "user"=>{"email"=>"info#blabl.nl"}, "artist_fee"=>"0.00"}, "commit"=>"Create Booking", "locale"=>"en"}
booker: {"name"=>"tttt", "date"=>"02-10-2014", "artist_fee"=>"0.00", "venue_id"=>""}
I have no idea why the order is different, also, it seems to but "cutting off" after the venue_id, which is obviously causing the error. Has anyone seen behavior like this before?
Edit:
Here's my booking_params private method
def booking_params
params.require(:booking).permit(*policy(#booking || Booking).permitted_attributes)
end
And the pundit policy;
def permitted_attributes
[:status,
:date,
:name,
:get_in_time_string,
:soundcheck_time_string,
:dinner_time_string,
:show_time_string,
:show_time_end_string,
:artist_fee,
:venue_id,
:venue_name,
:act_ids => [],
user: [ :id, :email ]
]
end
Like I said, same code works fine in development. The only way I can sort of reproduce the problem is by removing the user params from my permitted_attributes method, but then I actually get an "unpermitted parameters" error. What would be the right way to define the permitted attributes for "user"? Totally lost on this one.
I suspect your strong parameters are incorrect/are missing the nested user. If you post them I can fix that side of it, but an easy temporary fix is to simply use
params[:user][:email]
i.e access the parameters directly. Going back to the issue, have a look here for using nested parameters in the strong parameters:
Rails 4 - Strong Parameters - Nested Objects
Never found out what the problem really was. Not using Pundit for the strong params fixed it though.

Link for action problem

I have problem with my application.
I have table report, there are 2 column , user_id and comment_id
I created link on article comment view
<%= link_to "[ ! ]", report_comment_url(comment) %>
class CommentsController < ApplicationController
def report
#comment = Comment.find(params[:id])
#comment = CommentReport.new(params[:comment_report, :comment_id])
if #comment_report.save
redirect_to :back
end
redirect_to :back
end
end
but it was error
ActionController::MethodNotAllowed
Only post requests are allowed.
Do you have any suggestion how to post current_user id and comment_id to report table ?
Given what I assume you're trying to accomplish, I'd suggest you use link_to_remote.
params[:comment_report] is nil because there's no reference to that in your link_to statement. Since you mention in the comment that your view is:
<%= link_to "[ ! ]", report_comment_url(comment), :comment_id => comment.id, :user_id => comment.user_id %>
Then you need this in your controller:
#comment_report = CommentReport.new(:user_id => params[:user_id], :comment_id => params[:comment_id])
But I agree with NSD that link_to_remote would work better for what you want to accomplish (which is create a new record and return the user to the page). You would also eliminate the need for your #comment = Comment.find(params[:id]) statement.
Here's what's going on:
Using Restful routes, you've set up report as a post operation. Which seems reasonable because report is performing the create action.
Unfortunately link_to doesn't know or even care about that. Links in general only perform get requests. Forms produce post requests, but they seem unnecessary in this case.
You have four options.
Make the [ ! ] link a button on a form submitting to report.
Break RESTful guidelines and redefine report receive get requests.
Make this a link_to_remote call. N.B. This relies on javascript and will not work at all if Javascript is disabled.
Add the method options to the link_to call. N.B. This also relies on javascript and will fall back to a get request if javascript is disabled.
<%= link_to "[ ! ]", report_comment_url(comment), :method => :post %>
However none of these solutions will solve all your problems. There are a couple of bugs with your posted code that you may not have realized yet.
First:
#comment = CommentReport.new(params[:comment_report, :comment_id])
is bad syntax and will fail. There are a number of ways to fix this, the preferred method is to to roll :comment_id into the params[:comment_report] hash to fix this.
Ie pass the params as:
params = {
:id => 4, # done by report_comment_url
:comment_report => {
:attribute1 => value1,
...
:comment_id => 4
}
}
Now you can use
#comment = CommentReport.new(params[:comment_report])
for the desired effect.
Second:
report_comment_url does not pass along the additional parameters, so your controller will try to save an empty record. Adding the comment_report to the arguments of report_comment_url will fix this problem.
This will perform a remote call requesting the report action in the comments controller, with the parameter hash required to fix the other problem.
<%= link_to_remote "[ ! ]", report_comment_url(comment,
:comment_report => {:attribute1 => value1, ..., :comment_id => comment.id}),
:method => :post %>

Resources