Rails - Nested Objects Deletion - ruby-on-rails

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 %>

Related

Rails - Searching/showing available option when creating new instance of an object

I've got a one to many relationship with one movie per list entry, where a movie can be used in a list entry. My list_entries table has a movie_id and a list_id.
database schema
I've nested list_entries in lists so I can pass the list_id directly when creating a new instance.
Rails.application.routes.draw do
devise_for :users
root to: 'pages#home'
resources :movies, only: [:new, :index, :create, :show, :destroy, :edit, :update]
resources :users, only: [:show, :edit, :update]
resources :lists, only: [:new, :create, :show, :index, :destroy] do
resources :list_entries
end
end
Right now I can create and destroy list entries but I have to specify the movie id manually.
The UX I want to achieve is for the user to be able to search for movies from themy list_entries/new form but I don't even know where to begin.
The form as it is now:
<%= simple_form_for #list_entry, url: list_list_entries_path do |f| %>
<%= f.input :comment %>
<%= f.input :movie_id %>
<%= f.submit "Add", class: "btn devise-button" %>
<% end %>
My list entries controller:
class ListEntriesController < ApplicationController
before_action :find_list, only: [:index, :create, :show, :new, :destroy]
def new
#list_entry = ListEntry.new
end
def index
#list_entries = ListEntry.where(list: #list)
end
def show
#list_entry = ListEntry.find(params[:id])
end
def destroy
#list_entry = ListEntry.find(params[:id])
#list_entry.destroy
redirect_to list_list_entries_path
end
def create
#list_entry = ListEntry.new(entry_params)
#list_entry.list_id = params[:list_id]
if #list_entry.save
redirect_to list_list_entries_path
else
render 'new'
end
end
private
def find_list
#list = List.find(params[:list_id])
end
def entry_params
params.require(:list_entry).permit(:comment, :movie_id)
end
end
If you don't want to manually specify a movie_id in the form, you can use the simple_form_for association helper:
<%= f.input :comment %>
<%= f.association :movie %>
I believe it should be labeled based off of the movie title, but if not, you may have to specify a #to_label method in your Movie model.
Alternatively, you could query for the movies in your #new action and use them to do whatever you like in your view:
def new
#list_entry = ListEntry.new
#movies = Movie.all # or whatever query you think is relevant
end
The #collection_select documentation might be useful here:
https://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/collection_select

The action 'destroy' could not be found for ListingsController

UPDATE - I'VE NOW SOLVED THIS PROBLEM - I created a partial for the each Course item and rendered them from the main listing view. Thanks for all your help, I'm not really sure why it worked but it did END OF UPDATE
Apologies if this looks like a repeat posting but I've tried applying solutions to similar questions and they haven't worked, I'm stuck! Any suggestions welcomed, thank you.
Problem
I have a 'Courses' model which belongs to a 'Listings' model. The courses are created and deleted on a page belonging to Listing i.e. "/listing/2/courses"
Error Message
No route matches [DELETE] "/listings/2/courses"
Courses Controller def destroy detail
class CoursesController < ApplicationController
before_action :authenticate_user!, except: [:show]
before_action :set_listing
before_action :set_course, except: [:index, :new, :create]
def destroy
#course = #listing.courses.find(params[:id])
#course.destroy
flash[:notice] = "Course deleted!"
redirect_back(fallback_location: request.referer)
end
private
def set_course
#listing = Listing.find(params[:listing_id])
#course = Course.find(params[:id])
end
def set_listing
#listing = Listing.find(params[:listing_id])
end
def course_params
params.require(:course).permit(:name, :curriculum_type, :summary, :address, :course_places, :start_date, :finish_date, :price)
end
end
listing/listingID/courses page detail
<%= #listing.courses.each do |f| %>
<div class="jumbotron">
<ul>
<li>Name = <%= f.name %></li>
<li>Type of course = <%= f.curriculum_type %></li>
<li>Number of places = <%= f.course_places %></li>
<li>Start Date = <%= f.start_date %></li>
<li>Finish Date = <%= f.finish_date %></li>
<li>Price (£) = <%= f.price %></li>
<%= link_to "Delete Course", listing_courses_path(#listing, #course), method: :delete %>
</ul>
</div>
<% end %>
Routes.rb detail
resources :users, only: [:show]
resources :listings, except: [:edit] do
member do
get 'listing'
get 'pricing'
get 'description'
get 'photo_upload'
get 'amenities'
get 'location'
get 'courses'
end
resources :courses, except: [:edit] do
member do
get 'listing'
get 'pricing'
get 'description'
get 'photo_upload'
get 'amenities'
get 'location'
end
end
end
<%= link_to listing_course_path(#listing,f), :method => :delete, :data => { :confirm => 'Are you sure?' } %>
or try
<%= link_to listing_course_path(#listing,id: f.try(:id)), :method => :delete, :data => { :confirm => 'Are you sure?' } %>
route.rb
resources :listings do
resources :courses
end
By default, destroy method expects an ID as it's a member route. You are using listing/listingID/courses route without an ID. For that you need to define listing and/or courses as singular resources (read this) like:
resource :courses do
:
end
as described in this answer or make destroy a collection route like so:
resources :courses, except: [:edit] do
delete :destroy, on: :collection
:
rest...
end
Try and see if this works.
By the way, this looks a bit redundant as you are iterating over each #listing.courses and calling courses#destroy where you are destroying all the courses of #listing anyhow. So, why do #listing.courses.each in the first place. You should have either used a listing#destroy_courses method or remove #listing.courses.each iteration.
Update path in link_to to listing_course_path(#listing, #course) from listing_courses_path(#listing, #course)
<%= link_to "Delete Course", listing_course_path(#listing, f), method: :delete %>

Need a hand in Routing in rails

I'm trying to call a certain method "custom" in a controller called orders_controller.rb
but there is an error in my routing which is this
No route matches {:action=>"custom", :controller=>"orders"} missing required keys: [:id, :id]
this is my routes.rb file
Rails.application.routes.draw do
resources :categories
devise_for :users,:controllers => { :registrations => "registrations" }
resources :products
resource :cart, only: [:show] do
post "add", path: "add/:id",on: :member
get :checkout
end
resource :results, only: [:index] do
get :index
end
resources :comments
resource :home, only: [:index] do
get :index
end
resources :orders, only:[:show,:index,:create] do
post "custom", path: "custom/:id",on: :member
end
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
get 'results/index'
get 'comments/index'
root 'home#index'
end
and this is my orders_controller.rb
class OrdersController < ApplicationController
before_filter :initialize_cart
def create
#order_form=OrderForm.new(user: User.new(order_params[:user]),cart: #cart)
if #order_form.save
redirect_to products_path,notice:"Thank You for placing the order sir."
else
render "carts/checkout"
end
end
def custom
#order_form=OrderForm.new(user: current_user,cart: #cart)
if #order_form.save
redirect_to products_path,notice:"Thank You for placing the order sir."
else
render "carts/checkout"
end
end
private
def notify_user
OrderMailer.order_confirmation(#order_form.order).deliver
end
def order_params
params.require(:order_form).permit(
user:[:name,:phone,:address,:city,:country,:postal_code,:email])
end
end
in this i want to call the custom method when i click the place order button but i cant click it because the page is not opening
this is my main code of the page in views/cart/checkout.html.erb
<%if current_user %>
<%= button_to 'Place order',custom_order_path,:class => "btn btn-success btn-lg" %>
<%else%>
<div style="width:70%;margin:20px auto;">
<%=render "errors"%>
<%=render "form"%>
<%end%>
can someone tell me what is the routing error in this
You need to change route to collection_route as you are creating order.
resources :orders, only: [:show, :index, :create] do
post :custom, on: :collection
end
and now you can use
<%= button_to 'Place order',custom_order_path, :class => "btn btn-success btn-lg" %>

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.

Unable to create posts in rails forum on the topics page

I am new to rails and am trying create a forum. The forum has many topics, topics belong to a forum and have many microposts, and microposts belong to both topics and users. However, no matter what I try, the posts will not be created. Currently when I try to post, I get the routing error "No route matches [GET] "/topics""
My routes.rb file:
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :microposts, only: [:create, :destroy]
resources :forums, only: [:index, :show]
resources :topics, only: [:show]
_micropost_form.html.erb
<%= form_for(#micropost) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.hidden_field :topic_id, value: #topic.id %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.text_field :summary, placeholder: "One-line summary..." %>
<%= f.text_area :content, placeholder: "Compose a new post..." %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
microposts_controller.rb
class MicropostsController < ApplicationController
before_action :signed_in_user, only: [:create, :destroy]
before_action :correct_user, only: :destroy
def create
##topic = Topic.find_by_id(params[:topic_id])
#micropost = current_user.microposts.build(micropost_params)
if #micropost.save
flash[:success] = "Your solution has been posted!"
redirect_to topic_path(#topic)
else
redirect_to topic_path(#topic)
end
end
def destroy
#micropost.destroy
redirect_to root_url
end
private
def micropost_params
params.require(:micropost).permit(:summary, :content, :user_id)
end
def correct_user
#micropost = current_user.microposts.find_by(id: params[:id])
redirect_to root_url if #micropost.nil?
end
end
As you can see, I commented out the first line in my create function because I've tried posting based on the the micropost's relationship to the topic to no avail. Thanks in advance and let me know if it would help if I posted more code!
In your :topics resource, you didn't defined the index method, that's why you won't be able to get to topic's list or index page. Try to change your route like this:
resources :topics, only: [:index, :show]
or remove only attribute from resources, it will automatically include all your methods by default.
resources :topics
Also if you have relationship between models, you should define nested routes in your routes
file, For example, you can define them like this, you can change them accordingly:
try to change your route file like this:
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :forums do
resources :topics do
resources :microposts, only: [:new, :create, :destroy]
end
end
In above case, you can access your forums like this:
http://localhost:3000/forums
you can access your topics like this:
http://localhost:3000/forums/id/topics
you can access your microposts like this:
http://localhost:3000/forums/id/topics/id/microposts
If you want to access /microposts directly you have to put it outside any resource.
resources :microposts, only: [:index]
now you will be able to access it:
http://localhost:3000/microposts
Hope it will help. Thanks.

Resources