So, I have this form declaration:
<%= form_for 'students_list', {:url => update_students_list_stream_url(#stream), :method=>:patch} do |students_list_form| %>
Just as described in API docs, but this leads me to error:
No route matches [POST] "/streams/26/edit-students-list"
So it still tries to post, even though my HTML input has:
<input type="hidden" name="_method" value="patch" />
From Rails guide:
Rails works around this issue by emulating other methods over POST
with a hidden input named "_method", which is set to reflect the
desired method:
I'm quite confused
I was looking for an answer on why rails loaded method patch as post in the rendered form.
If you ended up here looking for that like I did, this is the answer you are looking for:
https://stackoverflow.com/a/46699512/5750078
From https://guides.rubyonrails.org/form_helpers.html#how-do-forms-with-patch-put-or-delete-methods-work-questionmark:
Rails use Rack::MethodOverride middleware that tweaks the HTTP methods PUT/PATCH to POST for supporting old browsers.
This can happen on Rails API application to unload the middleware for performance. In some cases where you want to call a casual PUT/PATCH request using form_with tag in the views, simply add
# config/application.rb
config.middleware.insert_after Rack::Runtime, Rack::MethodOverride
You'll be better doing this:
<%= form_for #stream do |student_form_list| %>
If you've set up your routes using the standard resources directive, you'll have the following routes:
Of these routes, the update path should just be students_list_stream_path -- not the update_students_list_stream_path you have now.
If you've set up the form_for to use the correct object, it will automatically set the path & method for update.
Related
Update
This was a legacy app I inherited, and I found out that the previous developers had removed the rack code that converted browser POST requests into PUT/PATCH based on the _method param that Rails adds to your forms.
# config/application.rb
# This is the line that caused the problem...
config.middleware.delete ::Rack::MethodOverride
Once I removed that line and restarted the server, things worked as expected.
Original Question
When I post a Rails form using the standard resources in the routes file, it raises a route not found error when I'm trying to update an existing record:
No route matches [POST] "/admin/lookups/record_types/1"
The model is namespaced as app/models/lookups/record_type.rb
# model file
module Lookups
class RecordType < ApplicationRecord
# ...
end
end
# form in view file
<%= form_with model: #record_type, scope: :record_type, url: [:admin, #record_type], local: true do |form| %>
<%= form.text_field :value %>
<% end %>
# Request being sent
POST admin/lookups/record_types/1
{ record_type: { _method: "patch", value: "value" } }
# in routes .rb
namespace :admin do
namespace :lookups do
# Does not work
resources :record_types
# Works when explicitly written out
post "record_types/:id", controller: record_types, action: :update
end
end
When I explicitly write out the POST request in the routes.rb file, it works as expected.
I know that Rails is actually POSTing the request and using the _method hidden attribute to map the routes file. However, something isn't converting that request properly.
It's an application I inherited, and at one point it was exclusively an JSON API (no direct UI), so I'm wondering if there was something removed that converted the Rails _method param to the proper controller? I don't know what that would be, though.
This is the output of my rake routes:
admin_lookups_record_types
GET /admin/lookups/record_types(.:format)
admin/lookups/record_types#index
POST /admin/lookups/record_types(.:format)
admin/lookups/record_types#create
new_admin_lookups_record_type
GET /admin/lookups/record_types/new(.:format)
admin/lookups/record_types#new
edit_admin_lookups_record_type
GET /admin/lookups/record_types/:id/edit(.:format)
admin/lookups/record_types#edit
admin_lookups_record_type
GET /admin/lookups/record_types/:id(.:format)
admin/lookups/record_types#show
PATCH /admin/lookups/record_types/:id(.:format)
admin/lookups/record_types#update
PUT /admin/lookups/record_types/:id(.:format)
admin/lookups/record_types#update
DELETE /admin/lookups/record_types/:id(.:format)
admin/lookups/record_types#destroy
The problem seems to be the use of the scope argument to the form_with method.
If you take a look at the routes you'll note that the route to the update action uses PUT or PATCH whereas the route to the create action uses POST.
As the documentation of the FormHelper module states:
in the options hash. If the verb is not GET or POST, which are natively supported by HTML forms, the form will be set to POST and a hidden input called _method will carry the intended verb for the server to interpret.
But nesting the special _method key inside record_type breaks this mechanism. Is the scope really necessary? I'd try removing it, it should work fine without it. The correct HTTP verb to use would be PUT or PATCH. Adding an additional POST route breaks the regular Rails structure without any real gain.
New to rails. Was working on chapter 2 of railsTutorial.
I was pair programming with someone who only uses text based browser w3m. So once we created user resource, we created a new user and tried deleting it but couldn't. I could do it on my Chrome browser though, so thought this may be issue with JS or so.
How do we resolve this?
Rails relies on javascript for links to http DELETE actions.
link_to "Delete Thing", thing_path(#thing), method: :delete
Generates a link with a data-method attribute. The Rails UJS driver then catches the click and turns it into into an AJAX HTTP DELETE request.
method: symbol of HTTP verb - This modifier will dynamically create an
HTML form and immediately submit the form for processing using the
HTTP verb specified. Useful for having links perform a POST operation
in dangerous actions like deleting a record (which search bots can
follow while spidering your site). Supported verbs are :post, :delete,
:patch, and :put. Note that if the user has JavaScript disabled, the
request will fall back to using GET. If href: '#' is used and the user
has JavaScript disabled clicking the link will have no effect. If you
are relying on the POST behavior, you should check for it in your
controller's action by using the request object's methods for post?,
delete?, patch?, or put?.
http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to
Added:
The whole reason we do this is that Rails is built around restful interfaces using the full range of HTTP methods; GET, POST, DELETE, PUT, PATCH. But some browsers can only send GET & POST requests and HTML does not define links which perform other actions than GET.
If no javascript is a requirement you can create a form to send requests other than GET:
<%= form_for #thing, method: :delete do |f| %>
<%= f.submit 'Delete thing' %>
<% end %>
Note that this form uses method="post" for compatability. Rails adds a special hidden input to the resulting form:
<input name="_method" type="hidden" value="delete" />
Which Rails uses to fake the extended set of HTTP methods (DELETE, PUT, PATCH, etc).
If you don't want to use Turbolinks with your Rails(4) application, it's easy! Just do this:
Remove the gem 'turbolinks' line from your Gemfile
Remove the //=require turbolinks from your app/assets/javascripts/application.js
Remove the two "data-turbolinks-track" => true hash key/value pairs
from your app/views/layouts/application.html.erb
I'm working through an older tutorial that was done for Rails 3. I'm using Rails 4.1.4.
One of the instructions is to change the routes file to include the following:
get '/boards/:board_id/conversations/:id/reply' => "conversations#reply", :as => :reply_board_conversation
post '/boards/:board_id/conversations/:id/reply' => "conversations#save_reply", :as => :reply_board_conversation
Obviously that gives me an error:
Invalid route name, already in use: 'reply_board_conversation'
It seems to me that the route is somehow trying to replicate the behaviour of a new and create action. Get for new and Post for create with a single route.
The problem is I can't figure out how to rewrite the route so it works. I've googled for solutions but can't seem to find anything. If anyone could point me in the right direction it would be hugely appreciated.
It looks like the only issue is with the duplicated "named route" name reply_board_conversation. So you could simply change one. I'd probably rename the save version to save_reply_board_conversation. Then it should work. Just remember to refer to the route this way in the future. This would primarily be used in a form tag. So, for exmaple:
<= form_tag :url => save_reply_board_conversation_path do %>
Note the use of save_reply_board_conversation_path instead of reply_board_conversation_path given that the form would be submitting a POST request instead of a GET request.
The names for these routes should be different although since the composition of the URL is the same so you really only need a name for the first one.
The trick with named routes is they generate the URL only, they do not set the HTTP request method. That has to be done independently.
That means you can call the same named route two different ways:
<%= link_to('View', board_path(#board)) %>
<%= link_to('Delete', board_path(#board), method: :delete) %>
These actually render as the same URL but one will hit the GET route, the other the DELETE one.
I try to add new controller and model use name foo and foos_controller, hope foos_path can redirect. Doesn't work.
A origin code here (working):
href="<%= contacts_path %>"
After I add new controller and model follow name convention I try use the same (Not working):
href="<%= foos_path %>"
And this contacts_path is not defined anywhere else in rb project.
what does xxxx_path mean and how to use it?
Rails follows convention to handle roots of application
when we execute this command
rails g scaffold foo
it generates routes along with your model, controller and views.
it generates a line in routes.rb as
resources :foo
this line makes you access all the actions of your controller
for example:
foos_path: # redirects you to the index page of your foos controller
new_foo_path: # redirects you to the create page of your foos controller etc.,
please go through this link for reference: http://guides.rubyonrails.org/routing.html
If you go to your terminal and type rake routes, it will list your currently defined routes. On the left side you'll have their prefix. For example, contacts might route to the index action in ContactsController.
The path suffix is a way of referring to those routes inside your code.
If foos_path is giving you an error, that means you either have not yet defined that route for the resource, or you have but it is called something else (if you defined it manually with the as: option).
More info can be found in the Rails Guide for Routing.
You'll be best reading up on this documentation
Path Helpers
Basically, the path_helpers of Rails are designed to help you define routes for your links etc in the most efficient way possible.
The bottom line is the path helper will take routes defined in your config/routes.rb and then allow you to call them dynamically - IE:
#config/routes.rb
resources :photos #-> photos_path
The path names are typically from your controllers, allowing you to route to the various actions inside them. As Rails is built around being a resourceful structure, it will by default create routes for the "standard" controller actions:
link_to
In order to use the path helpers effectively, you'll be best using the rake routes command in cmd, or by simply typing an invalid url into your app's address bar
I notice you're using the standard HTML <a href=""> tag in your question. You'll be much better suited to using the link_to helper of Rails:
<%= link_to "text", your_path %>
I am using Rails 4.
I have a stream model which has the following routing code in routes.rb:
namespace :admin do
resources :streams, param: :stream_id
end
I get the following routes:
admin_streams GET /admin/streams(.:format) admin/streams#index
POST /admin/streams(.:format) admin/streams#create
new_admin_stream GET /admin/streams/new(.:format) admin/streams#new
edit_admin_stream GET /admin/streams/:stream_id/edit(.:format) admin/streams#edit
admin_stream GET /admin/streams/:stream_id(.:format) admin/streams#show
PATCH /admin/streams/:stream_id(.:format) admin/streams#update
PUT /admin/streams/:stream_id(.:format) admin/streams#update
DELETE /admin/streams/:stream_id(.:format) admin/streams#destroy
For new stream there is no problem, rails generates the correct form attributes for the create method.
My problem is when I try to generate a form for update. As mentioned in this answer, I code of the form is:
<%= form_for #stream do |f| %>
:
:
<% f.button %>
<% end %>
However, this is what rails generates:
<form accept-charset="UTF-8" action="/streams/xxxx" class="edit_stream" id="edit_stream_4" method="post">
As you can see, from some reason the method Rails choose is post instead of put.
I know I can override the method manually, but I find it hard to believe that this is what I need to do. Any suggestions?
That is totally correct. Take a look at this:
http://guides.rubyonrails.org/form_helpers.html#how-do-forms-with-patch-put-or-delete-methods-work-questionmark
It's just a Rails workaround to the fact that not all browsers support PUT method. So Rails emulates POST method but knows it is really a PUT.
If you better check your form, you'll find a hidden field like this:
<input name="_method" type="hidden" value="put" />
HTML form tag takes only get or post as values for method attribute.
See Documentation for form tag.
In order to support RESTful routes other than GET and POST(already supported by form) i.e., PATCH, PUT and DELETE requests, Rails uses a work around by creating a hidden input field in the form which it later tracks down to decide the type of HTTP request.
For eg:
<input name="_method" type="hidden" value="patch" />
By default the form_for helper uses POST. But the form_for method can detect if the object passed to it has been persisted or not. If it has persisted, then it recognizes that you are doing an edit and specifies a PATCH method on the form.If not it automatically uses POST! I hope this helps!