Rails no route match POST url on update with single resource - ruby-on-rails

I'm creating a Rails application where there is a resource used as general settings for the whole website. I set everything as follows:
config/routes.rb
authenticate :user do
scope '/admin' do
resource :basics #This is my resource's name, put in singular
# ...
end
end
controllers/basics_controller.rb
private
# Use callbacks to share common setup or constraints between actions.
def set_basic
#basic = Basic.first
end
views/basics/_form.html.erb
<%= form_with(model: #basics, local: true) do |form| %>
Here is what rake routes shows about this resource:
new_basics GET /admin/basics/new(.:format) basics#new
edit_basics GET /admin/basics/edit(.:format) basics#edit
basics GET /admin/basics(.:format) basics#show
PATCH /admin/basics(.:format) basics#update
PUT /admin/basics(.:format) basics#update
DELETE /admin/basics(.:format) basics#destroy
POST /admin/basics(.:format) basics#create
I don't have any trouble making the form show up. However, when I submit it, I get the following error:
No route matches [POST] "/admin/basics/edit"
When I look at the HTML generated form, I can see that it passes "/admin/basics/edit" as an action while it should be just "/admin/basics" in my situation.
What should I do to make this work?
Thank you in advance

It looks like maybe you have the wrong instance variable passed to the form_with method? IIRC, Rails assumes the form action should be the same as the current path if the model argument is nil.
#basics is not defined (at least in the controller code you shared). Replacing it with #basic might work for edits (when Basic.first exists), but not create (if Basic.first doesn't exist).
You could try initializing #basic in your controller like this
def set_basic
#basic = Basic.first_or_initialize
end
Then pass it to form_with
<%= form_with(model: #basic, local: true) do |form| %>

Related

How to put query params on _path rails_helper

The problem is to pass Query params throuth the helper _path .
Example I know how to pass a normal param
edit_survey_path(#poll_module)
which generates
/survey/12 because of route /survey/:id
Which is right and okay for editing
However, I want to generate the following url for new_survey_path
/survey/new?pollModule=lgpd
so how should I write the
new_survey_path(????)
At the view's controller I have the #poll_module = 'lgpd'
Then I trided
new_survey_path(#poll_module)
new_survey_path({#poll_module})
new_survey_path({pollModule: #poll_module})
new_survey_path({:pollModule => #poll_module)
new_survey_path(:pollModule, {pollModule: #poll_module})
yes some are crazy, but it were things I have found on foruns
new_survey_path(#poll_module) generates
http://rubyenv:3000/surveys/new.lgpd?
that's because the route for edit is
#routes
new_survey GET /surveys/new(.:format) so is gets on the format
I want a query param
/surveys/new?pollModule=lgpd
because on the new survey I need to search the righ questions before answering the survey, and this question depende on the module
The problem it was not the _path helper, but the componet I was using on the view
if you use the button_to, it has already a parameter for parameters
{poll_module: 'teste123'} %>
doc ref ->
https://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-button_to
or if i you use the link_to it get the query by default
doc -ref->
https://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to
Normally you pass a query param like this new_survey_path({key: "value1", key2: "value2"}). So the one version you have reported that you tried should have worked. Thats why i assume it could interfere with your routes. Can you try to isolate the problem and simply add the following to your project:
# routes.rb
resources :surveys, only: [:new]
# app/controllers/surveys_controller.rb
class SurveysController < ApplicationController
def new
#pollModule = params[:pollModule]
end
end
<!-- app/views/surveys/new.html.erb -->
<h1>New Survey</h1>
<p>poll module inserted as query param: <%= #pollModule %></p>
<%= link_to "testLink", new_survey_path(pollModule: "randomInput") %>
When you click on the "testLink" it should redirect you to the URL localhost:3000/surveys/new?pollModule=randomInput.
Also be sure that the route is at the top of your routes.rb file, since sometimes the ordering is what messes things up. Hope that helpes and let me know if it worked.

Rails 5.1+ routing: `resources` not working but explicit route definition does

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.

Rails form builder with admin namespace

The code below works well to create new transactions on the Invoice show view. It however doesn't work when in admin namespace. i.e /admin/invoices/1/ but works on /invoices/1/
show.html.erb
<%= form_for([#invoice, #invoice.transactions.build]) do |form| %>
....
transactions form input
routes.rb
resources :invoices do
resources :transactions
end
When calling form_for in a namespace route like /admin/invoices/1/, Rails will automatically append admin to your route. In other words, form_for([#invoice, #invoice.transactions.build]) would POST to a route like /admin/invoice/:id/transactions/ rather than /invoice/:id/transactions/.
To fix, explicitly set the URL of the form and use a route helper method to infer the correct route:
form_for(#invoice, url: invoice_transaction_url(#invoice.id))
Note that you may need to replace invoice_transaction_url with the correct route. Use rake routes to find the helper method that corresponds to the desired controller POST action.

route is redirecting to wrong path

This is what my routes currently look like:
which gives
On my homepage I have a create vacancy button
<%= link_to "plaats", new_employer_vacancy_path(:employer_id)%>
Which should be linked to the line from the first image
get '/employers/:employer_id/vacancies/new', to: 'vacancies#new', as: 'new_employer_vacancy'
In the vacancies_controller#new - create I have:
def new
#vacancy = Vacancy.new
#employervacancy = Employervacancy.new
end
def create
#vacancy = Vacancy.create(vacancy_params)
createEmployervacancy
redirect_to employer_vacancy_path(current_employer, #vacancy)
end
def createEmployervacancy
#employer = current_employer
Employervacancy.create(vacancy_id: #vacancy.id, employer_id: #employer.id)
end
But whenever I click the button I get redirected to some other method in my vacancies_controller that is totally irrelevant.
How is this even possible? Don't I clearly define that when that path is clicked he should go to vacancies#new? and not to vacancies#show_specific_employer_vacancies?
EDIT
After following the answers I am indeed being linked to the correct route.
First, it gave me this error.
After trying to pass the current_employer.id instead of #employer like suggested I got following error:
For your routes, you'd better to change into nested route for easily maintaining routes.
Remove these codes:
get '/employers/:employer_id/vacancies/:id', to:"vacancies#show_specific_employer_vacancies", as: "employer_vacancy"
get '/employers/:employer_id/vacancies/edit/:id' ...
get '/employers/:employer_id/vacancies/index' ...
get '/employers/:employer_id/vacancies/new' ...
path '/employers/:employer_id/vacancies/:id' ...
change into:
resources :employers do
resources :vacancies
end
Try to use basic routes here because you use standard simple form url. For example:
<%= simple_form_for(#employee, #vacancy) %>
The simple_form_for will generate url well if you use nested routes above.
Finally, in your link you have to add #employer_id
<%= link_to "plaats", new_employer_vacancy_path(:employer_id => #employer_id)%>
I hope this help you
Your router cannot tell the difference between your employer_vacancy and new_emplyer_vacancy routes because the :id parameter accepts anything. Because of this, when you point your browser to "/employers/5/vacancies/new", the route is taking your employer_vacancy route and assigning {:employer_id => 5, :id => "new"} instead of going to your new_employer_vacancy route (because routes are first-come-first-serve).
To correct this, add a constraint to your first route to ensure that only numbers (and not the string "new") is accepted into the employer_vacancy route:
get '/employers/:employer_id/vacancies/:id',
to: 'vacancies#show_specific_emplyer_vacancies',
as: 'employer_vacancy',
constraints: { id: /\d+/ } # <- This line
As Wes Foster said rails router is trying to find a first match.
It means that given a path /employers/999/vacancies/new your router looks through the routes and when it sees get '/employers/:employer_id/vacancies/:id he thinks that this route matches. So :employer_id is 999 and :id is new.
I'd suggest to put the route with :id at the end of employers routes:
...
get '/employers/:employer_id/vacancies/new'
...
get '/employers/:employer_id/vacancies/:id'
Btw this is better than adding a constraint because:
It is easier
It doesn't pollute routes file
Later you may want to change ids to be hashed or alphabetic and then you'd have to change the constraint

undefined method `pushes_path' for #<#<Class:0x007f85a15c6c90>

i'v been trying to resolve this error for the past 5 hours and I'm gonna burn my computer if I can't solve this.
undefined method `pushes_path' for #<#:0x007f859d605250> this is the error code I'm getting but i don't understand why.
this is my index.html.erb file inside of the interaction
<%= simple_form_for #push do |f| %>
<%= f.input :payload, as: :text %>
<%= f.input :segment, as: :radio_buttons %>
<%= submit_tag "start the campaign" %>
<% end %>
and this is my interaction controller
class InteractionController < ApplicationController
def index
#push =Push.new
end
end
Push is my table in the database and i'll get the inputs and write them in the database to use them for later one.
and this is my routes file
devise_for :partners
get 'home/index'
get 'segmentation/index'
get 'interaction/index'
root to: "home#index"
i really don't know why its looking for pushes_path, what am i doing wrong?
form_for
The problem you have is that your form_for method is going to try and generate a route based off your #path object. And as such, if you don't have a path created for it, you'll receive the error you're getting:
:url- The URL the form is to be submitted to. This may be represented
in the same way as values passed to url_for or link_to. So for example
you may use a named route directly. When the model is represented by a
string or symbol, as in the example above, if the :url option is not
specified, by default the form will be sent back to the current url
(We will describe below an alternative resource-oriented usage of
form_for in which the URL does not need to be specified explicitly).
The bottom line is that as Rails is object orientated, its built around the assumption that you'll have routes set up to handle the creation of individual objects.
Every time you use form_for, Rails will attempt to construct your routes from your object -- so if you're trying to do the following, it will treat the routes as photo_path etc:
#app/views/pushes/new.html.erb
<%= form_for #push do |f| %>
...
<% end %>
--
Fixes
As #mandeep suggested, there are several fixes you can employ to get this to work:
Firstly, you can just create a route for your push objects:
#config/routes.rb
resources :pushes
Secondly, as you're using a different controller, you'll want to do the following:
#config/routes.rb
resources :interactions
#app/views/pushes/new.html.erb
<%= form_for #push, url: interaction_path do |f| %>
...
<% end %>
This will route your form submission to the interactions controller, rather than the pushes controller that you'll get by default!
Objects
Something to consider when creating Rails-based backends is the object-orientated nature of the framework.
By virtue of being built on Ruby, Rails is centered on objects - a term for a variable, which basically encompasses much more than just a piece of data. Objects, in the case of Rails, are designed to give the application:
Once you understand this, the entire spectrum of Rails functionality becomes apparent. The trick is to realize that everything you do in Rails should be tied to an object. This goes for the controllers too:
--
Ever wondered why you call resources directive in your routes, for a controller? It's because you're creating a set of resourceful routes based for it:
Do you see how it's all object orientated?
This gives you the ability to define the routes for specific controllers etc. The most important thing to note is how this will give you the ability to determine which routes / controller actions your requests should go
--
There's nothing wrong in using the controller setup as you have - the most important thing is to ensure you're able to define the custom URL argument, as to accommodate the non-object based structure
In your index action you have
def index
#push =Push.new
end
and your form has
<%= simple_form_for #push do |f| %>
so your form is looking for /pushes with post verb or pushes_path and you don't have that route in your routes.rb file so to fix this you need to add this in routes.rb:
resources :pushes
Update:
when you add resources :push rails basically creates seven different routes for you. One of which is
POST /pushes pushes#create create a new push
and if you look at the html generated by your form it would be something like:
<form action="/pushes" class="new_push" id="new_push" method="post">
// your fields
</form>
notice the action and verb so when you submit your form your routes are checked for them and since you didn't define them in your routes you were getting this error
And how will i be able to use the params i m getting from this form with this new resource addition?
Your form will take you to pushes_controller create action so first of all you'll have to define them. You can access them simply by params[:pushes] in your controller action but since you want to create a new record so you'll have to permit those attributes, checkout strong parameters
If you are using rails >= 4 then you can do
class PushesController < ApplicationController
def create
#push =Push.new(push_params)
if #push.save
redirect_to #push
else
render 'interaction/index'
end
end
private
def push_params
params.require(:push).permit(:attributes)
end
end
If you are using rails < 4 then instead of permitting these attributes(because strong parameters feature came from rails 4) you'll have to tell rails that these attributes are accessible by writing this in your pushes.rb
attr_accessible :attribute_name
Why it is assuming that its pushes controller?Because of the Push.new creation?
That's because if you look at your index action #push = Push.new so #push contains a push object with nil values(as you have just initialized it) so this is where rails magic comes, rails automatically tries to figure out url of your form and since your #push is only an initialized variable so rails takes you to create action for it. For details you should checkout rails polymorphic urls If you want your form to go to interaction_controller or some other url then you'll have to specify the url option for it
<%= form_for #push, url: "your_url_for_custom_method" %>
// other fields
<% end %>
And in the end you should really read docs

Resources