I have controller ArticlesController.
I have resource articles (in routes.rb file).
Why action articles#new corresponds GET /articles/new request and action acticles#create corresponds POST /articles request. Why not POST /articles/new ?
The new action displays a view in which you have a form that you fill out and submit. In this case, its a form for articles. This form could be anywhere on your website, but Rails convention holds that it is in your new page.
The create action takes the information you submitted in the form and tries to create an object. In this case, an article. This action does not, by convention, display a view, but redirects to another page. For me, it's usually the show page of that newly created article.
It's defined in the RESTful architecture that a POST request to a collection URI (e.g. "/articles") should create a new entry.
As already noted, the "/articles/new" URI is simply displaying the form, and it's not really an element URI (in the RESTful sense). Therefore it would be inproper to POST, PUT or DELETE it.
See http://en.wikipedia.org/wiki/Representational_state_transfer
Related
I'm trying to determine the best way to handle form submissions and routing.
I have the standard _form.html.erb, as well as new.html.erb and edit.html.erb - each renders form, subscription = #subscription, which is declared in the new and edit controller action.
The model is subscription, and this is my form element in _form.html.erb (it's in the checkout namespace) -
<%= form_for([:checkout, subscription]) do |f| %>
/checkout/subscriptions/new is the URL for a new subscription. When you submit the form, it posts to /subscriptions.
The problem is that if there are errors, and you refresh the browser (I know users will do this for one reason or another), you get an error message because there is no view for /subscriptions/index (I don't need one).
Same applies for editing. If you go to (as an example) /checkout/subscriptions/14/edit, everything's great. But if you submit the form, it posts to /subscriptions - same problem as with new - if you refresh the page, you get the no view error. This is the way Rails handles it with a new scaffold.
So my questions are:
what's the url I should use in the form_for tag?
what should I do for routing?
and how can I avoid this error when refreshing the page?
This is how the rails version of REST works (and should work). The key here is the HTTP method.
Reloading the page creates a GET request. While you controller should create resources with POST and update with PATCH/PUT.
GET requests should be idempotent (not create, alter or delete resources) so a GET request for /subscriptions or /subscriptions/:id is very different.
What can you do:
Use the window.beforeunload event in javascript to warn the user that they will lose data.
Route the GET /subscriptions path to subscriptions#new.
Route the GET /subscriptions/:id path to subscriptions#edit.
Can anyone tell me why when rendering a page in rails, for example:
render 'controller/action'
the url displays
/domain/controller
instead of
/domain/controller/action
The right page is shown, but the url is just the controller.
Is this an error that I have somewhere in my app?
for example if I did:
render 'users/show'
then the users show page would be displayed, but the url would be:
/mydomain/users
instead of what I would expect:
/mydomain/users/show
This is because Rails uses REST extensively for URL routing.
For eg:
When your application receives request as GET users/1, routes.rb will be referred to find out the corresponding controller and action.
Suppose, your route file contains
get '/users/:id', to: 'users#show'
Then, you request will be handle by show action of users controller.
For more information on routing in rails, refer to the Guide.
EDIT:
To answer your question, why doesn't the "show" action appear in the url,
Rails applications adhere to REST architectural constraints, called RESTful routes which are defined using a combination of HTTP verbs and URLs to controller action
When creating or updating a resource, the target URL by default is the same like the index route, for users e.g. localhost:3000/users.
The difference is that instead of POST, PUT method is used (as far as I know).
I find this suboptimal. For example, when having a navigation menu with an item "Create user" in it, I want to set the active CSS class when the item is active. I do this by comparing current_page? to the menu item's URL (e.g. users/new). This works fine, but only when I don't have validation errors. In this case, the URL now isn't users/new anymore, but users.
Is there a good reason why Rails doesn't point to users/new (or users/edit) respectively for POST/PUT requests? Are there any downsides to using them? Is there an easy way to change this behaviour?
The reason is REST.
In a nutshell, Rails treats everything as a resource, and there are conventions followed in order to do so. In a typical CRUD application, you have the Create (POST), Read (GET), Update (PUT/PATCH), and Destroy (DELETE) actions, which are the verbs used to act on resources.
Let's say you have a User resource. A list of users would be found at /users. An individual user, being an individual object of a resource, would then be found at /users/1, where "1" is the identifier of the resource in question.
Now, based on the CRUD verbs available to you, if you wanted to edit a user, what ACTION makes the most sense given the CRUD verbs we talked about? PUT /users/1 is the correct answer; you're updating (the U in CRUD) a particular resource. If you wanted to delete that user? DELETE /users/1 makes sense. If you want to create one? CREATE /users is the logical choice, because you're not acting on a particular object, but on the resource as a whole, similar to GET /users not acting on an individual object, but a collection.
/users/new is a path to a page that will let you do that, but it doesn't mean it should make the CREATE request to /users/new, because "new" doesn't describe a resource the same way.
From what I understand, these actions are usually triggered after a form is submitted. I can't imagine any reason why a form would generate json, in other words, (assuming a hypothetical controller is named 'UsersController') I can't imagine when or how a form would take my browser to:
localhost:3000/users.json
wouldn't post requests automatically take the user to:
localhost:3000/users
...and hence automatically to html? And furthermore, if they arrived here, at:
localhost:3000/users
and typed in:
localhost:3000/users.json
wouldn't this just be a GET request back to index.json? And hence back to the index action?...rendering json in that particular action via a GET request (not the create action, via POST)?
I'm confused and can't understand how anyone could ever end up at users.json from a POST request, and hence I can't imagine why a respond_to block that renders json makes sense in these actions. What am I missing?
Rails assumes that the controller actions might also be accessed as an API and not just via the browser. In such cases it makes sense to respond to those requests differently instead of redirecting the client (browser) to the index or show action.
When you create a resource from an API client, it might not make sense to redirect the user to the index or show action instead of just responding to the client that the resource was created (or not). Same applies for the update and destroy actions.
A copy action and copy.html.erb are defined in our rails 3.2 app. This copy action is to copy from the current record, allowing user modify slightly and submit for creation just like new. Here is the header of the copy.html.erb:
<%= form_for #engine_config, :as => :engine_config, :url => engine_configs_path do |f| %>
After clicking save, it hits create in controller and this is what we wanted.
Our question here is what the engine_configs_path stands for? Usually engine_configs_path is for index. Here the form is for create and is not index. What's the reasoning of this index path on create form?
When you do bundle exec rake routes you will see something similar to this;
engine_configs GET /engine_configs(.:format) engine_configs#index
POST /engine_configs(.:format) engine_configs#create
Which means engine_configs_path works with both GET for index controller action and POST for create controller action.
Therefore engine_configs_path refers to the url the form is posted to in the controller.
Form
You generally don't need to define the url argument in your form - Rails' form_for helper takes an initialized class object & extracts the required data for it automatically:
From the docs:
Typically, a form designed to create or update a resource reflects the
identity of the resource in several ways:
(i) the url that the form is
sent to (the form element's action attribute) should result in a
request being routed to the appropriate controller action (with the
appropriate :id parameter in the case of an existing resource)
(ii)
input fields should be named in such a way that in the controller
their values appear in the appropriate places within the params hash,
(iii) for an existing record, when the form is initially
displayed, input fields corresponding to attributes of the resource
should show the current values of those attributes.
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.
--
Route
Your specific question is regarding the route your form_for method will use
As per w3c standards, HTML forms are defaulted to POST data. This means whenever you use a form_for helper in Rails, it will automatically use the POST variant of your routes
As described by Acacia, if you're using resourceful routes, you'll get a series of routes like this:
This means you can call the same route (engine_configs_path) with the HTTP Verb of POST to send to a specific controller#action, which is what's happening for you
The behaviour of engine_configs_path depends on the HTTP verb. That is, an HTTP GET to engine_configs_path will show the index; an HTTP POST to engine_configs_path will create a new one. form_for can check whether #engine_config exists in the DB or not via #engine_config.new_record?, so it knows to do a POST instead of a PUT.