In a standard rails form_for like below, it will return an html form where the action is /posts/1/comments. But including the post.id is not good for security, instead i want it to use the post.token for that record instead.
Rails form_for
<%= form_for([#post, Comment.new]) do |f| %>
<%= f.text_field :text %>
<% end %>
Html of form_for
<form class="new_comments" id="new_video_marker" action="/posts/1/comments" accept-charset="UTF-8" method="post">
<input type="text" name="comments[text]" id="comment_text">
</form>
You need to create a token.
$ rails generate migration AddTokenToPost post_token:string
Modify your post.rb :
# app/models/post.rb
class Post < ApplicationRecord
before_create :set_post_token
private
def set_post_token
self.post_token = generate_post_token
end
Try this for your form :
<%= form_for([#post, Comment.new]) do |f| %>
<%= f.hidden_field :token %>
<%= f.text_field :text %>
<% end %>
But including the post.id is not good for security
Who fooled you into thinking that?
This is a classic example of security by obscurity. Sure somebody can't just guess that the url is /posts/1/comments but if you're not authorizing the resource properly /posts/somereallylongtoken/comments is no more secure. If not UUID's would be magically more secure and there would be no one using auto-incrementing integers for PKs.
You can use single use tokens which are placed in the URL for stuff like user confirmations but thats only because you're sending a link in a email and you're limited to GET. Thats used as a nonce to authorize the user. Not just to obfuscate an ID.
I am trying to make a form where you select tables that you want to export. I made a simple form with a list of tables that can be exported. My plan was to allow the user to toggle check boxes for the tables they want to export and as a result they would be able to download a zip file containing the tables.
Currently, when I try to go to the page with the form, I get an error:
undefined method 'model_name' for nil:NilClass
The majority of the usage of simple forms that I see online consists of using forms to create new items to save in their models. As a result, it seems that the line simple_form_for #example would mean that when the user clicks the submit button, there is a line in the controller such as #example = SomeClass.new". My understanding is that the user input of the form is saved in #example and can be used by the controller. However, as I am not creating a new item in the model, I just want to use the values from #example, I am not sure what to put in the controller to get rid of the error so that I can code the rest of the function in the controller.
Controller:
class FormController < ApplicationController
def index
#options = []
print(#options)
end
end
The form used:
<h2>Which tables do you want to export?</h2>
<div class="well">
<% tables_in_model = %w(Table1 Table2 Table3) %>
<%= simple_form_for #selected_options, :url => form_index_path, :method => :get do |f| %>
<%= f.input :options, as: :check_boxes, collection: tables_in_model %>
<%= f.button :submit, as: :Submit %>
<% end %>
</div>
As you said correctly in your question, simple_form should be used to render forms to the user when her actions are related to the creation or edition of ActiveRecord models.
For instance, when writing down code to enable a search feature, where your goal is to simply pass a bunch of user chosen params to a controller, you should not use it. I believe you are in a similar position with the feature you described.
Simple solution though: use rails form related DSL to get your form going!
Hope it's the answer you needed. Feel free to ask for more details if needed. Cheers!
i think you are using simple_form you need to specify like
#selected_options = SelectedOptionModel.new(params)
into your controller
then it passes into View.
if you don't have any model you can use form_tag
like this:
<%= form_tag("/search", method: "get") do %>
<%= label_tag(:q, "Search for:") %>
<%= text_field_tag(:q) %>
<%= submit_tag("Search") %>
<% end %>
this will create html form like this:
<form accept-charset="UTF-8" action="/search" method="get">
<input name="utf8" type="hidden" value="✓" />
<label for="q">Search for:</label>
<input id="q" name="q" type="text" />
<input name="commit" type="submit" value="Search" />
</form>
On a model, I have attr_accessor: :email_settings.
In a view, I have:
<%= form_for some_model do |f| %>
<%= f.fields_for :email_settings do |email_settings| %>
<%= email_settings.label :general, _("General updates") %>
<%= email_settings.check_box :general %>
General site updates
<% end %>
<% end %>
But in the HTML, this does not create one input for some_model[email_settings][general], it creates two. One hidden and one a checkbox:
<label for="user_email_settings_general">General updates</label>
<input name="user[email_settings][general]" type="hidden" value="0">
<input id="user_email_settings_general" name="user[email_settings][general]" type="checkbox" value="1">
General site updates
What's going on here? Why is there a hidden input and a checkbox for the same value, when I only want a checkbox?
Also in the controller action that the form is submitted to, I do this:
def update
puts "email_params: #{email_params}"
end
def email_params
params.require(:user).permit(:email_settings)
end
Which outputs:
Unpermitted parameters: email_settings
email_params: {}
Not sure how email_settings is being interpreted as "unpermitted" when I'm explicitly permitting it.
From the browser should always send a response.
If you do not select the box, will send the hidden field to understand that something has been sent.
In this way the rails will always receive an indication that the box was selected or not.
similar answer is here: Why does the check_box form helper generate two checkboxes, one hidden?
In simple_form is possible to use the http delete verb instead the default post verb?
<%= simple_form_for #object , method: :delete do |f| %>
<%= f.input :instance_name, as: :check_boxes, collection: #roles %>
<%= f.button :submit %>
<% end %>
It doesn't works.
Unfortunately simply stating that it doesn't work is not helpful in understanding the problem you're seeing, but I'll make a guess based on my own initial confusion with the "method:" parameter. Most browsers don't support PUT and DELETE methods, so what simple_form_for does is generate a form with a POST method, but it also adds a hidden field to pass the actual method. So:
simple_form_for #service, url: service_path, method: :delete
generates:
<form action="/services/6" method="post">
<input name="_method" type="hidden" value="delete" />
....
Rails uses that to call the correct controller method. Hope that helps.
Added on edit, 2013-02-11:
I should make it clearer that the problem I'm having is not that I can't get the ERB code I write to produce correct HTML code. Rather, it is that my Rails installation sometimes interprets the HTML code correctly, so that clicking the link has the right effect, and sometimes incorrectly, producing a "No route" error. More details in my answer below.
Original question, 2013-02-08:
I'm making a simple login form using Rails 3.2.11. I've tried several ways of coding the Submit button of the form, but each has failed due to a missing route error. I got one method to work, and checked in my code. However, now the very same code fails.
This is the code of app/views/session/new.html.erb (I'm using simple_form, but not its button wrapper):
<h2>Log in</h2>
<%= simple_form_for :session do |f| %>
<%= f.input :username %>
<%= f.input :password %>
<%= button_to "Submit", session_index_path %>
<% end %>
The relevant part of the HTML code that this produces is:
<h2>Log in</h2>
<form accept-charset="UTF-8" action="/session/new" class="simple_form session" method="post" novalidate="novalidate">
<div style="margin:0;padding:0;display:inline">...</div>
...the input fields...
<form action="/session" class="button_to" method="post">
<div>
<input type="submit" value="Submit" />
<input name="authenticity_token" type="hidden" value="...token value here..." />
</div>
</form>
</form>
This is my config/routes.rb:
MyApp::Application.routes.draw do
resources :at_user
resources :session, :only => [:new, :create, :destroy]
match 'login' => 'session#new', as: :login
match 'logout' => 'session#destroy', as: :logout
root to: 'main#index'
end
This is what the command rake routes outputs:
at_user_index GET /at_user(.:format) at_user#index
POST /at_user(.:format) at_user#create
new_at_user GET /at_user/new(.:format) at_user#new
edit_at_user GET /at_user/:id/edit(.:format) at_user#edit
at_user GET /at_user/:id(.:format) at_user#show
PUT /at_user/:id(.:format) at_user#update
DELETE /at_user/:id(.:format) at_user#destroy
session_index POST /session(.:format) session#create
new_session GET /session/new(.:format) session#new
session DELETE /session/:id(.:format) session#destroy
login /login(.:format) session#new
logout /logout(.:format) session#destroy
root / main#index
The target path of button_to is session_index_path, which should cause the create method of SessionController to be called -- and for a while, it did. Now, after I've restarted Rails, pressing the button instead produces an error page, with the text
No route matches [POST] "/session/new"
For some reason, Rails has started to think that the target of button_to is session#new instead of session#create. It's as if it thinks that the HTTP method it's supposed to call is GET instead of POST -- however, the HTML code shows that the method is post.
By the way, another thing I earlier tried was to give button_to the action and method parameters, as documented here:
<%= button_to "Submit", options: {action: 'create', method: :post} %>
then this is what's generated:
<form action="/session/new?options%5Baction%5D=create&options%5Bmethod%5D=post" class="button_to" method="post">
which doesn't look like what I want, either. button_to's default HTTP method is POST, which appears in the result, but the :options hash is just tacked onto the end of the URL, and the word create appears nowhere else.
The first answer to this question says that, unlike what the documentation says, one should not put the parameters of button_to in a hash, but give them directly. So, I tried this:
<%= button_to "Submit", action: 'create' %>
However, the action create still does not show up in the generated HTML:
<form action="/session" class="button_to" method="post">
So, those were things I tried before attempting to using the named path method, which worked for a minute, but for some mysterious reason, doesn't anymore.
Any ideas what I'm doing wrong? (It's probably something obvious I've overlooked.)
The button_to documentation is actually wrong in a few places (Note my answer here: Why is this button_to rendering incorrectly in Rails 3.2.11?). To fix that Submit button, use this:
<%= button_to "Submit", { action: 'create' }, method: :post %>
The options and html_options hashes need to have explicit separations defined using the {} braces depending on what you are trying to accomplish.
The create action won't show up in the URL since a POST HTTP method to /session is routed to the create action.
So far, the method suggested by Cyle in a comment above seems to work best, namely, adding url: session_index_path to the simple_form_for declaration:
<h2>Log in</h2>
<%= simple_form_for :session, url: session_index_path do |f| %>
<%= f.input :username %>
<%= f.input :password %>
<%= button_to "Submit" %>
<% end %>
I haven't yet experienced a route error when clicking the "Submit" button produced by this code.
I'm not accepting this answer, though, because the base problem, which I haven't solved yet, is the fickleness of Rails; that is, code that has worked before suddenly stops working. For instance, today I first tried this code:
<%= simple_form_for :session do |f| %>
<%= f.input :username %>
<%= f.input :password %>
<%= button_to "Submit1" %>
<%= button_to "Submit2", session_index_path %>
<% end %>
Pressing the "Submit2" button worked. However, when I removed the line that contained button "Submit1", stopped and restarted the Rails server (as I've done every time between attempts), and reloaded the page, pressing the Submit2 button produced a "No route" error, even though the HTML code of the button remained the same:
<form action="/session" class="button_to" method="post">
<div>
<input type="submit" value="Submit2" />
<input name="authenticity_token" type="hidden" value="...this value was the same also..." />
</div>
</form>
Very puzzling. Might be related to the details of my setup, though it isn't that exotic.
Added on edit:
When I took look at the relevant part of the HTML that is produced by Cyle's method, I noticed that it's the same as for almost all the other cases:
<form accept-charset="UTF-8" action="/session" class="simple_form session" method="post" novalidate="novalidate">
<div style="margin:0;padding:0;display:inline">
<input name="utf8" type="hidden" value="✓" />
<input name="authenticity_token" ... />
</div>
...the username and password input fields...
<form action="/session" class="button_to" method="post">
<div>
<input type="submit" value="Submit" />
<input name="authenticity_token" type="hidden" value="voOGIAbJfwvjxVRbk02V5l6zn6iwvOiMvaeauPBIrqU=" />
</div>
</form>
So it might be just luck that this has worked for me so far.
The problem was most probably that after I created the application, I decided to change the names of a couple of the classes and the associated files. This resulted in class and file names that didn't follow the Rails convention on pluralizing nouns in certain contexts. For instance, I had a file session_controller.rb that contained a class named SessionController; the correct names are sessions_controller.rb and SessionsController.
I created a dummy Rails application, and then commanded rails generate scaffold AtUser and rails generate scaffold Session, and then used the resulting filenames and identifiers as a guide for correcting the actual application. Now, I don't get those "No route" errors anymore. The command rake routes outputs:
at_users GET /at_users(.:format) at_users#index
POST /at_users(.:format) at_users#create
new_at_user GET /at_users/new(.:format) at_users#new
edit_at_user GET /at_users/:id/edit(.:format) at_users#edit
at_user GET /at_users/:id(.:format) at_users#show
PUT /at_users/:id(.:format) at_users#update
DELETE /at_users/:id(.:format) at_users#destroy
sessions POST /sessions(.:format) sessions#create
new_session GET /sessions/new(.:format) sessions#new
session DELETE /sessions/:id(.:format) sessions#destroy
login /login(.:format) sessions#new
logout /logout(.:format) sessions#destroy
root / main#index
Before this, I saw a "No route" error also when I included a file in another file without having required the former at the top of the latter. After performing the renaming fix, I tried to reproduce this error, and it did reoccur: Rails mistakenly reports a missing route, when the actual problem is a missing require statement. This suggests that Rails's error reporting mechanism is buggy.
Thanks to user bullfrog for pointing me in the right direction.