simple_form_for: Removing root model name from params - ruby-on-rails

When I create a form using simple_form_for #model, upon submit the post params has all the attributes grouped under params[model]. How do I get simple_form to drop this grouping and instead send it directly (under params root)?
<%= simple_form_for #user, do |f| %>
<%= f.input :name %>
<%= f.input :password %>
<%= f.submit %>
Now the name and password attributes would normally be sent under params[:user][:name], params[:user][:password] etc. How do I get simple_form to post these as params[:name], params[:password] etc.?
Thanks!
Ramkumar
ps: In case you are wondering why I need this, the bulk of my app is to serve as an API and I have built a set of methods to validate a request which expect some attributes to be in root. In a rare instance (forgot password), I actually need to present a form and I am looking for a way to use these methods.

you can explicitly define the name for an input by passing input_html to it:
input_html: { name: :name }
(needed this myself for sending an resource to a thirdparty endpoint with redirect to my side which relied on the plain attribute names, but i actually wanted not to built up label and input via the tags ;) )
also see simple form builder impl

Two ways I can think of:
The first is, don't use simple_form to build your form, but do it by hand or with the form_tag and *_tag methods. These will allow you to more closely specify what parameters are used in your form.
If you want to keep simple_form, though, then have it call a different controller action. Refactor the controllers to strip out the logic into a separate method. Something like:
class UsersController
def create_from_api
controller_logic(params)
end
def create_from_form
controller_logic(params[:user])
end
def controller_logic(params)
[actual work happens here]
end
end

Related

Is a link_to a custom route or a form the best way to provide state change?

What is the 'Rails way' to provide access to methods such as the following from a view
def approve!
self.update_attribute status, 'approved'
end
Is it best to create a link to a custom route
<%= link_to 'Approve', approve_object_path(#object) %>
#objects_controller.rb
def approve
#object.approve!
end
Or to create an update form
<%= simple_form_for #object do |f| %>
<%= f.input :status, input_html { value: 'approved' }, as: :hidden %>
<%= f.submit %>
<% end %>
On the one hand, using a form and not using the approve! method at all seems to align better with restful routes.
On the other hand, a link to a custom route seems to provide less opportunity for submitted values to be manipulated by the user, and also requires less code to implement.
Which is the preferred way to do this?
I don't know if there's a preferred best practice, per se...
Just my opinion, but I normally do the link_to approach, and for an "state machine" like your example. The need for an entire form for a simple action like this is a lot of extra code that isn't necessary when an action can be called to change the state.
The counter argument to this is that it breaks CRUD, and requires a non-CRUD route. Convention over configuration champions would probably prefer an entire new controller to change the state of the object.
TL;DR - I do the link_to approach, and I use :remote => true to make it asynchronous so the page doesn't even reload (unless you need the page to redirect elsewhere).
You can change state remotely with both the scenarios.
But I think if only a state has to be changed then use link_to. As we don't need to have form features with listed attributes in params here.

Why is my Rails form helper written improperly?

I know I've written it wrong, but I'm looking at the documentation and can't figure out how.
My model is Quote and has three fields, body, attribution, and work, all strings. The form is intended to add a new quote to a page of quotations.
on main/index.html.erb
<%= form_for(:quote, url: {action: 'create'}) do |f| %>
<%= f.text_field :body %>
<%= f.text_field :attribution %>
<%= f.text_field :work %>
<%= submit_tag "Submit" %>
<% end %>
in main_controller.rb
def create
Quote.create(body: params[:body], attribution: params[:attribution], work: params[:work])
end
The form submits, and an entry is saved to the database -- but it's a totally blank entry. I'm not sure why. Help would be appreciated!
Three things:
The way rails forms are supposed to work, you're not meant to get body, attribution, etc independently, they should be wrapped up into a quote object. But...
In your form, your not properly binding an object to the form the way rails expects. You can read more in the documentation here: http://guides.rubyonrails.org/form_helpers.html#binding-a-form-to-an-object. You could also generate a fake scaffold rails generate scaffold HighScore game:string score:integer to generate a fake model and see an example of how it's supposed to work. The default scaffolding even has simple examples of how to deal with save errors.
Finally, as #Paven suggested, when you get confused, be sure to look at what's going on in your log - i.e. what params are being posted to your create action. That is always helpful and a good way to diagnose problems quickly.
Your form does't need the action argument. The form_for helper uses ActiveRecord objects to determine the path, meaning as long as you build your object correctly, you won't need to determine your path individually:
<%= form_for #quote do |f| %>
Secondly, you'll want to look at your create method:
#app/controllers/quotes_controller.rb
def new
#quote = Quote.new
end
def create
#quote = Quote.new(quote_params)
end
private
def quote_params
params.require(:quote).permit(:body, :attribution, :work)
end
The problem is you're not sending an ActiveRecord object to your form_for helper. You can read the explanation here:
In Rails, this is usually achieved by creating the form using form_for
and a number of related helper methods. form_for generates an
appropriate form tag and yields a form builder object that knows the
model the form is about. Input fields are created by calling methods
defined on the form builder, which means they are able to generate the
appropriate names and default values corresponding to the model
attributes, as well as convenient IDs, etc. Conventions in the
generated field names allow controllers to receive form data nicely
structured in params with no effort on your side.
In order to get the form working correctly, you need to be able to provide a valid ActiveRecord object (#variable), which the helper can use to determine the url etc
My code above helps you provide a new ActiveRecord variable, and allows you to use it in the form. This should allow the form_for method to send your data to the create method, which will then create & save an object in the db for you

how form_for works in Ruby on Rails

I am an newbie. I have read the API documentation. But still don't understand how form_for works.
Firstly, from Ruby on Rails Tutorial, the form for follow button:
<%= form_for(current_user.relationships.build(followed_id: #user.id)) do |f| %>
<div><%= f.hidden_field :followed_id %></div>
<%= f.submit "Follow", class: "btn btn-large btn-primary" %>
<% end %>
I understand current_user.relationships.build(followed_id: #user.id) means a new record. But why can we not just submit and trigger controller to save the record without hidden_field? Why do we still need to post followed_id to controller?
Secondly, in hidden_field, what does :followed_id means? I believe that is a symbol, i.e. it equals only "followed_id" not a variable of id. If that is only the name of the input field, then what is its value?
Thirdly, how does form_for know where the submission should be sent to? Which controller and action the form_for will post to?
Fourth, how does params work with form_for? In this follow button case, params[:relationship][:followed_id] will return #user.id in controller. How does it know the first hash attribute is :relationship? We have neither mentioned form_for :relationship nor form_for #relationship.
I know these questions can be very dumb, but I am really stuck. Any help will be appreciated.
I didnt do that tutorial so mind me if i dont answer directly to your question.
Take a look at the rails guide about form helpers and it explains in details your questions, probably in a more articulate way than i can.
form_for(path/to/your/controller/action) is a helper method to create HTML form elements with the url path to the POST or GET request. The helper knows if it should be a new record or an update record based on what you are asking to do in your controller action.
For example
In your controller
def new
#my_instance_variable = Myobject.new
end
In your view new.html.erb
<%= form_for #my_instance_variable do |f| %>
...
<% end %>
In your case the logic was directly written in the helper and you could also directly write
<%= form_for Myobject.new %>
Both will result with the following html
<form action="/myobjects/new" method="post">
# in this case rails knows its a `POST` request because the route new action
# is by default a POST request. You can check these routes and their request
# by using `rake routes` in terminal.
Then the hidden_field is another helper to contain a value, in your case the #user.id that will be passed as parameter then saved as a Create or update action for the given object. The reason it doesnt add the value in the hidden field tag is because you already have a model association that knows the id of user since the link of form uses the build method with user id.
Last part you need to understand the form_for link logic
current_user.relationships
# implies the association of the current_user has many relationships
current_user.relationships.build
# .build is a method to populate a new object that can be save as a new record
# means you will create a new relationship record by populating the user_id
# column with the current_user.id and the followed_id with the target #user.id
After reading the book The Rails 4 Way, I understand form_for better now.
11.9.1.5 Displaying Existing Values.
If you were editing an existing instance of Person, that object’s attribute values would have been filled into
the form.
in this way, when we build the relationship by usingcurrent_user.relationships.build(followed_id: #user.id), the relationship instance will be created and gain attribute followed_id. So that, instead of "creating" a relationship, we are actually editing the relationship by the form.
Then Rails will know you are editing and load the existing attribute "followed_id" to the field. Therefore, we don't need to assign value to the field like using f.hidden_field :followed_id, value: #user.id.
And the reason why we have to use a field to pass followed_id to params is because HTTP server is stateless, it doesn't remember you are creating a relationship with which user.
One of the advantages of writing form_for current_user.relationships.build(followed_id: #user.id) instead of standard form_for #relationship is we don't need to write "if-condition" in controller like this:
unless current_user.nil?
if current_user.following?(#user)
#relationship=current_user.relationships.find_by(followed_id: #user.id)
else
#relationship=current_user.relationships.new
end
end
params will be sent to the controller which belongs to the instance's model. "post" method will go to action create, "delete" will go to destroy, "patch" will go to update, etc.
params will be a hash with another hash inside like { instace_name: { field_1: value1, field_2:value2 } } or full params as below
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"afl+6u3J/2meoHtve69q+tD9gPc3/QUsHCqPh85Z4WU=",
"person"=>{"first_name"=>"William", "last_name"=>"Smith"},
"commit"=>"Create"}

Rails 3 / Form without Model: How do I create a form that is not tied to a model?

I have a model, and I have a view that displays a form for creating a new object based on that model. Let's call that form, Form1.
Once the user submits Form1, the object is created. I then want to display, on the following page, a second form Form2, which asks the user to check off various options before the object is saved to the database.
My problem is probably extremely basic. I don't know how to create Form2, given that it is not tied directly to the model. Because I am a Rails newbie, I have only created forms as following:
form_for(#object) { |f| ... }
#object is an object instantiated from the model
Problem: I do not believe that kind of code will work for my purposes here. How do I create Form2, given that it must not be based on #object or #object's model?
Some specifics from my app:
The site accepts values (Form1) before redirecting to an OAuth server.
When the user verifies her credentials on the OAuth server, she is redirected back to my site. An XML-RPC request then retrieves information about the user's account on the OAuth server.
The XML response may indicate that the user has only one account on the OAuth server. If so, some values are retrieved from the XML and added to the object--which is then (finally) saved in the database--and the user is redirected to a success page.
However, if the XML response indicates that the user has multiple accounts on the OAuth server, I want to display a form (Form2) that allows the user to select which accounts on the OAuth server to associate with my site. So Form2 really asks the user how many objects to create, rather than about specific attributes of an object.
Use form_tag instead of form_for, then use the appropriate form helpers: text_field_tag instead of f.text_field, text_area_tag instead of f.text_area, etc. Example:
<%= form_tag "/my_controller/update2" do %>
<%= text_field_tag "account", "default info" %>
<%= submit_tag "Save" %>
<% end %>
The Rails API site has a great reference to all of the _tag helpers: http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html
Starting in rails3, validations have been decoupled from ActiveRecord so you can create vanilla objects that can be used as validators with the form helpers:
class Person
include ActiveModel::Validations
attr_accessor :first_name, :last_name
validates_each :first_name, :last_name do |record, attr, value|
record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
end
end
and then in your template:
<%= form_for(Person.new) do |f| %>
...
It's a handy way to get the benefits of the form helpers for things like email forms without having to create a model object tied to your schema.
To create a table-less model,
class Person
include ActiveModel::Model
attr_accessor :first_name, :last_name
validates :first_name, :last_name, presence: true
end
Then in your view,
<%= form_for(Person.new) do |f| %>
.... your form ....
<% end %>
Another similar solution can be found at RailsCasts:
Active-Model
If you are looking to use SimpleForm then there is a specific answer for that here Does form_tag work with Simple_form?

How can you pass an object from the form_for helper to a method?

So let's say I have a form which is being sent somewhere strange (and by strange we mean, NOT the default route:
<% form_for #form_object, :url => {:controller => 'application',
:action => 'form_action_thing'} do |f| %>
<%= f.text_field :email %>
<%= submit_tag 'Login' %>
<% end %>
Now let's say that we have the method that accepts it.
def form_action_thing
User.find(????? :email ?????)
end
My questions are thus:
How can I make the object #form_object available to the receiving method (in this case, form_action_tag)?
I've tried params[:form_object], and I've scoured this site and the API, which I have to post below because SO doesn't believe I'm not a spammer (I'm a new member), as well as Googled as many permutations of this idea as I could think of. Nothing. Sorry if I missed something, i'm really trying.
How do I address the object, once I've made it accessible to the method? Not params[:form_object], I'm guessing.
EDIT
Thanks so much for the responses, guys! I really appreciate it. I learned my lesson, which is that you shouldn't deep-copy an object from a form, and that the parameters of a form are actually included when you submit it.
I will admit it's sort of disheartening to not know stuff that seems so obvious though...
you need to pass the "id" of your "#form_object" in the url and then lookup that object (assuming you have a model and using ActiveRecord)
It depends on how do you set up your routes. If you're using the default /:controller/:action/:id route, you can pass it as a parameter in the URL. Note that not the whole #form_object can/should be passed, but it's id or some other attribute to identify it instead. In this case, you should make your URL:
<% form_for #form_object, :url => {:controller => 'application',
:action => 'form_action_thing', :email => some_email} do |f| %>
<%= f.text_field :email %>
<%= submit_tag 'Login' %>
<% end %>
And in your controller
def form_action_thing
#user = User.find_by_email(params[:email])
end
You can pass parameters through the url, but when submitting a form the only thing that should (probably) be passed through the url is the record id for a RESTful record.
And it appears you didn't find out yet where your form data can be found in the params.
So
All the data from your form should end up in params[:form_object]. The actual value for :form_object is selected by Rails, it's probably coming from the object's class (too lazy to look that up right now)
In any case, you can easily find out where your form values are submitted by looking at your console/log output. All the params for each requests are dumped there.
The form fields will be inside the params like params[:form_object][:email] - each field that is submitted has an entry corresponding to the field name.
The params hash not contain all the original values from your #form_object. There will be only those values that you included in the form.
If you need to pass non-editable values to the controller with your form, use hidden_field(s) These will be submitted with the form, but are not visible to the user.

Resources