Button_to patch method is unexpectedly working - ruby-on-rails

I was struggling the whole day to make my button_to working. This is the code in my order index view:
<%= button_to("Remove", {:controller => "orders", :action => "update", :id => product.id}, :method => :patch) %>
my controller :
def update
#order.products.delete(Product.find(#product.id))
#product.ordinable = true
#product.save
#order.amount = 0
#order.save
#order_amountnew = #order.amount
#order.products.each do |x|
#order_amountnew = #order_amountnew + x.price
end
#order.amount = #order_amountnew
#order.save
if #order.products.empty?
#order.destroy
end
redirect_to orders_url
end
and my routes:
Rails.application.routes.draw do
root to: 'pages#home'
devise_for :users
resources :products
resources :orders, only: [:show, :create, :index, :destroy, :update]
post '/payment', action: :payorder, controller: 'orders'
patch '/orders', action: :update, controller: 'orders'
require "sidekiq/web"
authenticate :user, lambda { |u| u.admin } do
mount Sidekiq::Web => '/sidekiq'
end
end
I was trying to trigger the code inside the update method using the button_helper inside my order index. I come up with this solution and unexpectedly my button_to method now trigger the update method, but only after i spent hours googling and added to my routes this line of code:
patch '/orders', action: :update, controller: 'orders'
My question is now, whenever i want to trigger a method with a link_to or a button_to, i must create a route for each method like that right? Otherwise, i get routing error right? I have this doubt because here:
resources :orders, only: [:show, :create, :index, :destroy, :update]
i already defined an update route for my orders resource, so i don't understand why i need to specify again the route for the crud method i want to use. Thank you.

resources :orders, only: [:show, :create, :index, :destroy, :update] will yield update url as /orders/:id. You can verify that by poking in rake routes output.
patch '/orders', action: :update, controller: 'orders' yields update url as /orders.
The reason why latter works is because <%= button_to("Remove", {:controller => "orders", :action => "update", :id => product.id}, :method => :patch) %> send a request to /orders?id=.... You can verify that in the stacktrace
If you want to use 1, you should change button_to to format like <%= button_to("Remove", product_path(product), :method => :patch) %>. See https://api.rubyonrails.org/v5.2.1/classes/ActionView/Helpers/UrlHelper.html#method-i-button_to for references.
Hope that helps :).

Related

How to Create an Instance of Model A, from a Form inside a view for Model B

So I have a Message model, and a ChatRoom model.
When I display a chat room, I use the show action on the ChatRoom controller. In the view for this action, there is a little form for the user to create a post, and submit that post to the chat room being shown.
When I run my tests, however, I get an error "no route matches [POST] /messages/an_id_of_some_sort". Specifically, in this little test:
post message_path, params: {message: {body: "yo ho ho and a bottle of rum!"}}
assert_redirected_to chat_room_path(#channel)
the error pops up in the post message_path.
The show method on the chat room controller looks like
def show
if(#user = current_user)
#chats = #user.chat_rooms
#chosen = ChatRoom.find_by(id: params[:id])
if(#chosen.messages.any?)
#messages = #chosen.messages
else
#messages = nil
end
#message = Message.new
end
end
Then the little form bit of the view is:
<div class="message-input">
<%= form_for(#message) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.text_area :body, placeholder: "Write Message..." %>
<%= f.hidden_field :room, :value => params[:room] %>
<%= button_tag(type: "submit", class: "message-submit-btn", name: "commit", value: "") do %>
<span class="glyphicon glyphicon-menu-right"></span>
<% end %>
<% end %>
</div>
I have a create action on the Messages Controller which does the saving to the database:
#message = current_user.messages.build(message_params);
#message.chat_room = params[:room]
if #message.save
redirect_to chat_room_path(#message.chat_room)
end
and routing-wise I have
Rails.application.routes.draw do
root 'welcome#welcome'
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
get '/signup', to: 'users#new'
post '/signup', to: 'users#create'
get 'users/signup_success'
delete '/chat_rooms/leave/:id', to: 'chat_rooms#leave', as: 'current'
get 'welcome/welcome'
resources :users
resources :account_activations, only: [:edit] #Only providing an Edit route for this resource.
resources :password_resets, only: [:new, :edit, :create, :update]
resources :chat_rooms, only: [:new, :create, :show, :index]
resources :messages, only: [:create, :edit, :destroy]
end
I have tried playing around with explicitly setting the :url on the form_for, but no dice. There is another question on this problem, but the solution there hasn't really helped.
I would greatly appreciate any help.
With this line you're running POST /messages/:id
post message_path, params: {message: {body: "yo ho ho and a bottle of rum!"}}
In your routes file you have this:
resources :messages, only: [:create, :edit, :destroy]
This will create the routes POST /messages, PUT/PATCH /messages/:id, and DELETE /messages/:id. You can verify this with rake routes.
None of these generated routes handle POST /messages/:id.
If you're trying to have the test create a new message, then you can use messages_path instead. message_path (with singular message) takes a message argument as a message, e.g. message_path(Message.first) and uses that to build the url.

Model won't update on submission

Im getting this weird error when i want to update my model (model name carts).
Error : The action 'update' could not be found for CartsController
this is my carts_controller.rb :
class CartsController < ApplicationController
include CartForcable
before_action :scoped_cart, only: [:show, :update]
def show
end
private
def scoped_cart
force_cart! lambda {|r| r.includes(:entries => {:sign => [:dimensions, :substrates]})}
end
def update
#cart = #cart.find(params[:id])
if #cart.update_attributes(cart_params)
flash[:notice] = translate 'flash.notice'
else
flash[:error] = translate 'flash.error'
end
support_ajax_flashes!
respond_to do |format|
format.html # renders view
format.json { render json: #entry }
end
end
end
and these are my routes.rb :
resources :categories, only: [:index] do
resources :signs, shallow: true, only: [:index]
end
resources :carts, only: [:show, :update]#, param: :cart_permalink
resource :cart, as: :user_cart, only: [:show, :update], param: :cart_permalink do
resources :cart_entries, only: [:index, :create, :update, :destroy], as: 'entries', path: 'entries'
end
resource :user, only: [:edit, :show, :update], as: 'current_user', path: 'profile'
resources :signs, only: [:show]
resources :pages, only: [:show], param: :permalink
and my show.html.erb has this form :
<%= form_for #cart, :url => {:controller => "carts", :action => "update" } do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :regnum %>
<%= f.text_field :regnum %></br>
<%= button_to "Order/update", {:controller => "carts",:action =>
'update', :id => #cart.id }, :method => :update %>
<% end %>
After i enter name or number (for example), and click update button or something, it doesnt upload any data into model carts(which has the right columns).
Thanks,
Michael
Your update method is private; controller actions need to be public. Move
def update
#cart = Cart.find(params[:id])
...
end
to be above the
private
line. You need to do the find on the Cart model, not a #cart instance.
You can use a show action although edit would be more standard and fits right in with the RESTful routes without the need to override convention. The Rails routing guide should help here: http://guides.rubyonrails.org/routing.html
Additionally, you need to define the value of #cart in your edit action:
def edit
#cart = Cart.find(params[:id])
end
This will ensure it has a value on the edit form and so comes back into your update action through the parameters.
So if you try to edit a cart with something like /carts/12345/edit (where 12345 is the id of the cart you want to update) it should all hang together.
This looks wrong:
#cart = #cart.find...
don't you mean...?:
#cart = Cart.find...
And it seems you are using "cart_permalink" instead of "id" on your routes
resource :cart, as: :user_cart, only: [:show, :update], param: :cart_permalink do
Check the server log and see the name of the parameter that holds the ID, also run "bundle exec rake routes" to double check.

Custom update function routing for rails

I'm implementing a custom update function called update_status for my users controller. I need some help with the routing. what I want to do is update a status that only admins can access. I'm calling the update function via a form helper through the edit function in the users controller. This is my code for the form helper:
<%= form_for #user, :url => url_for(:controller => "users", :action => "update_status"), method: :put do |f| %>
<%= render "shared/error_messages", object: f.object %>
<%= f.check_box :admin %>
<%= f.label :admin %>
<%= f.check_box :editor %>
<%= f.label :editor %>
<%= f.submit "Save Changes", class: "btn btn-primary" %>
<% end %>
But when I click save changes all I get is this error
I want to route the action so that the user id can be resolved.
Controller action code:
def update_status
if #user.update_attributes(status_params)
flash[:success] = "User updated"
redirect_to #user
else
render 'edit'
end
end
Routes:
Transpub::Application.routes.draw do
resources :users do
member do
put 'update_status'
end
end
resources :papers
resources :comments
resources :reviews
resources :sessions, only: [:new, :create, :destroy]
resources :relationships, only: [:create, :destroy]
resources :comments, only: [:create, :destroy]
resources :subject_field, only: [:create, :destroy]
#get "users/new"
root "static_pages#home"
match "/signup", to: "users#new", via: "get"
match "/signin", to: "sessions#new", via: "get"
match "/signout", to: "sessions#destroy", via: "delete"
match "/help", to: "static_pages#help", via: "get"
match "/about", to: "static_pages#about", via: "get"
match "/contact", to: "static_pages#contact", via: "get"
match "/search_papers", to: "papers#index", via: "get"
match "/browse_papers", to: "papers#browse", via: "get"
In your routes files, look for the part that corresponds to the users controller and make sure that you have the following code
resources :users do
put :update_status, on: :member
end
That will declare the route. Another thing you have to update is the url of the form. Change the url to
form_for #user, :url => [:update_status, #user], html: { method: :put } do |f|

Couldn't find [model] without an ID - Ruby on rails

I have multiples forms in the Edit view like you can see below:
View
-#posts.each do |post|
.form
= simple_form_for post, url: update_posts_path do |f|
= f.input :title
= f.submit
Then when I'm updating any form I get this error 'Couldn't find Pots without an ID'. You can see the controller and the routes file here:
Controller
def update
#post = Posts.find(params[:id]) //If I change it for Posts.first then is working
if #post.update_attributes(params.require(:posts).permit(:title))
redirect_to ....
else
flash[:notice] = "Sorry."
render :edit
end
end
Routes File
resources :posts, :only => [:index, :edit, :update] do
get "edit", :on => :collection, :as => :edit
patch "update", :on => :collection, :as => :update
end
I think that the error is that for some reason this (#post = Posts.find(params[:id])) is returning a nil object then the controller can't update it. Can someone give me a hand?
The error is the :on => :collection part. This allows the action to be called without an id, so params[:id] is empty.
:on => :collection is normally used for index like actions that have a set of records as a result, so don't need the id part.
btw: the whole thing:
get "edit", :on => :collection, :as => :edit
patch "update", :on => :collection, :as => :update
is redundant, the first line:
resources :posts, :only => [:index, :edit, :update]
tells Rails everything it needs.
For update action you are using :on => :collection but your request is put/patch request so you will use patch "update", :on => :member, :as => :update also make changes in form_for as per your route.Use<%=simple_form_for post ,:url=> update_post_path(post) do |f| %> instead of url: update_posts_path
Regarding params.require(:posts).permit(:title)
First problem : you are using posts instead of post
second is Parameter :id is missing
you will use params.require(:post).permit(:title,:id) instead of params.require(:posts).permit(:title)

Rails - Nested Objects Deletion

I want to delete the nested object book, that is owned by a user. In the user#show page appears all the books related to that user. Besides each book there is a link to delete it. Here is my code:
routes.rb:
resources :users do
resources :books, :only => [:new, :create, :destroy]
end
book_controller.rb:
def destroy
#user= User.find(params[:user])
#book = Book.find(params[:book])
#book.destroy
redirect_to current_user
end
And in the user#show page:
<%= link_to "Delete", user_book_path(current_user, book), :method => :delete %>
I know this is wrong, but how can I do it in order to deleted the wanted book?
When you are deleting you can forget about the fact that it's a nested resource. You know which book you are talking about, so you can just delete it directly.
Routes:
resources :users do
resources :books, :only => [:new, :create]
end
resources :books, :only => :destroy
Book controller:
def destroy
#book = Book.find(params[:id])
#book.destroy
redirect_to current_user
end
View:
<%= link_to "Delete", book_path(book), :method => :delete %>

Resources