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 %>
Related
I am creating a page where a logged in user can view a list of their upcoming reservations and cancel a reservation if need be.
I've tried this as the destroy function:
def destroy
#trip = Reservation.find(params[:id])
#trip.destroy
flash[:alert] = "This booking has been cancelled."
redirect_to your_trips_path
end # destroy/ cancel a booking
which produces the following error
undefined method `space_reservation' for #<#<Class:0x00007f8198a191d8>:0x00007f8195341160>
Did you mean? space_reservations_url
Here is the relevant code in the controller.rb
class ReservationsController < ApplicationController
before_action :authenticate_user!
before_action :set_reservation, only: [:approve, :decline, :destroy]
...
def your_trips
#today = DateTime.now
#trips = current_user.reservations.order(start_date: :desc)
end
def destroy
#reservation = Reservation.find(params[:id])
#reservation.destroy
flash[:alert] = "This booking has been cancelled."
redirect_to your_trips_path
end # destroy/ cancel a booking
Routes.rb
resources :spaces, except: [:edit] do
member do
get 'listing'
delete :delete_image_attachment
get 'preload'
get 'preview'
get 'get_dates'
get 'get_times'
put :deactivate
put :activate
get 'browse_spaces'
end
resources :reservations, only: [:create]
resources :calendars
end
resources :reservations, only: [:approve, :decline, :destroy] do
member do
post '/approve' => "reservations#approve"
post '/decline' => "reservations#decline"
delete 'destroy'
end
end
View
<div class="panel-body">
<% #trips.each do |trip| %>
...
<div class="col-md-3 text-right trips-index">
<% if trip.start_date && trip.end_date > #today %>
<%= link_to 'Cancel',
space_reservation(trip),
method: :delete,
class:"btn btn-danger",
data: { confirm: 'Are you sure?' } %>
<% end %>
</div>
</div>
<hr/>
<% end %>
</div>
I would like for the reservation to be deleted when the cancel button is clicked and to no longer appear in the list of reservations. Does anyone spot what I did wrong in the function? Thanks.
The error message says that the space_reservation method is undefined.
In your view, you called that undefined method here:
<%= link_to 'Cancel',
space_reservation(trip),
method: :delete,
class:"btn btn-danger",
data: { confirm: 'Are you sure?' } %>
<% end %>
Assuming everything else works, if you either create the space_reservation method or find another way to resolve the issue, then it should work.
I have created a ToDoList based off Hartl's tutorial, and following a video and worded tutorial to add a tagging system. I have followed till Section 10, where they asked me to modify my new.html.erb file to the code as shown on the source. To improvise for structural differences in code, I would edit some other files, like in this case, my micropost_form partial instead. Occasionally, I alternated between the code in the video and code in the worded tutorial because some of them would produce error messages or would not produce the required functionality. Here are the files that I think are involved in this question.
_micropost_form.html.erb(The filling up form that would be displayed on the user's home page)
<%= simple_form_for #micropost do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.label :content %><br />
<%= f.text_area :content, placeholder: "Add new task..." %>
</div>
<div class="field">
<%= f.label :tag_list, "Tags (separated by commas)" %><br />
<%= f.text_field :tag_list %>
</div>
<%= f.submit "Add Task", class: "btn btn-primary" %>
<% end %>
micropost.html.erb(for showing the individual micro posts)
<li id="micropost-<%= micropost.id %>">
<%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
<span class="user"><%= link_to micropost.user.name, user_path(micropost.user) %></span>
<span class="content"><%= micropost.content %></span>
<p><small>Tags: <%= raw micropost.tags.map(&:name).map { |t| link_to t, tag_path(t) }.join(', ') %></small</p>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
<% if current_user?(micropost.user) %>
<%= link_to "Done", micropost_path(micropost), method: :delete, data: { confirm: "Keep up the good work!" } %>
<% end %>
</span>
</li>
routes.rb
Rails.application.routes.draw do
resources :users
resources :microposts
get '/about', to: 'static_pages#about'
get '/contact', to: 'static_pages#contact'
get '/signup', to: 'users#new'
post '/signup', to: 'users#create'
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
get '/users/admin', to: 'users#admin'
get 'tags/:tag', to: 'microposts#index', as: :tag
root 'static_pages#home'
end
micropost_controller
class MicropostsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
before_action :correct_user, only: :destroy
def index
params[:tag] ? #microposts = Micropost.tagged_with(params[:tag]) : #microposts = Micropost.all
end
def show
#micropost = Micropost.find(params[:id])
end
def create
#micropost = current_user.microposts.build(micropost_params)
if #micropost.save
flash[:success] = "Micropost created!"
redirect_to root_url
else
#feed_items = []
render 'static_pages/home'
end
end
def destroy
#micropost.destroy
flash[:success] = "You have deleted a task!"
redirect_to request.referrer || root_url
end
private
def micropost_params
params.require(:micropost).permit(:content, :tag_list, :tag,
{tag_ids: [] }, :tag_ids)
end
def correct_user
#micropost = current_user.microposts.find_by(id: params[:id])
redirect_to root_url if #micropost.nil?
end
end
Micropost model
class Micropost < ApplicationRecord
belongs_to :user
has_many :taggings
has_many :tags, through: :taggings
default_scope -> { order(created_at: :desc) }
validates :user_id, presence: true
validates :content, presence: true, length: {maximum: 140 }
attr_accessor :tag_list
def self.tagged_with(name)
Tag.find_by!(name: name).microposts
end
def self.tag_counts
Tag.select('tags.*, count(taggings.tag_id) as count')
.joins(:taggings).group('taggings.tag_id')
end
def tag_list
tags.map(&:name).join(', ')
end
def tag_list=(names)
self.tags = names.split(',').map do |n|
Tag.where(name: n.strip).first_or_create!
end
end
end
Tag model
class Tag < ApplicationRecord
attr_accessor :name
has_many :taggings
has_many :microposts, through: :taggings
end
static_pages controller
class StaticPagesController < ApplicationController
def home
if logged_in?
#micropost = current_user.microposts.build
#feed_items = current_user.feed.paginate(page: params[:page])
end
end
def help
end
def about
end
def contact
end
end
feed.html.erb
<% if #feed_items.any? %>
<ol class="microposts">
<%= render #feed_items %>
</ol>
<%= will_paginate #feed_items %>
<% end %>
I got the following error
ActionController::UrlGenerationError in StaticPages#home
No route matches {:action=>"index", :controller=>"microposts", :tag=>nil}, missing required keys: [:tag]
app/views/microposts/_micropost.html.erb:5:in `block in _app_views_microposts__micropost_html_erb___3891111682689684005_70324923859580'
app/views/microposts/_micropost.html.erb:5:in `map'
app/views/microposts/_micropost.html.erb:5:in `_app_views_microposts__micropost_html_erb___3891111682689684005_70324923859580'
app/views/shared/_feed.html.erb:3:in `_app_views_shared__feed_html_erb__3168328449514417483_70324923896060'
app/views/static_pages/home.html.erb:13:in `_app_views_static_pages_home_html_erb__3511776991923566869_70324898321240'
Can anyone suggest what might be wrong here? Please let me know if more information is needed.
Update: I have implemented some of the changes provided by the answer below, but still has not understood why :tag is not detected, and why the code in red is actually highlighted.
ActionController::UrlGenerationError in StaticPages#home
No route matches {:action=>"index", :controller=>"microposts", :tag=>nil}, missing required keys: [:tag]
The problem is you don't have an index route for your microposts.
Rails.application.routes.draw do
root 'static_pages#home'
get '/readme', to: 'static_pages#readme'
get '/about', to: 'static_pages#about'
get '/contact', to: 'static_pages#contact'
get '/signup', to: 'users#new'
post '/signup', to: 'users#create'
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
get '/users/admin', to: 'users#admin'
resources :users
resources :microposts, only: [:create, :destroy] #Here's the problem
get 'tags/:tag', to: 'microposts#index', as: :tag
end
change to:
resources :microposts, only: [:index, :create, :destroy]
EDIT:
Another problem is
if logged_in?
#micropost = current_user.microposts.build #this just returns a new 1
#feed_items = current_user.feed.paginate(page: params[:page])
end
You probably want something like:
if logged_in?
#microposts = current_user.microposts
#feed_items = Micropost.all.paginate(page: params[:page])
end
This will give you all the user's microposts. Then you iterate through them in your views.
I actually found the cause of the problem to be quite simple. After running rails console, it seems like my db:seed wasn't even raked properly, causing my tags to have nil names and causing me to be unable to find the route. Looking further into Rails console is adding nil instead of values to solve my seed adding problem, I realised I have added attr_accessor, forgetting that normal attributes should be added via the command line into the Migration instead of writing into the Model directly. Removing it according to the post updates my database and the code works.
I am attempting to trigger a destroy action from an index page for tags attached to multiple records. However, when the action is triggered I get the above error in the create action. The error does not occur when the create action is invoked. My code is as seen below.
Tag Controller
class TagsController < ApplicationController
before_action :require_user, only: [:edit, :update, :destroy]
before_action :set_search
def new
#tag = Tag.new
end
def create
tag = Tag.create(tag_params)
if tag.save
redirect_to tags_path
else
redirect_to sign_up_path
end
end
def destroy
#tag = Tag.find(params[:tag_id])
#tag.destroy
redirect_to tags_path
end
private
def tag_params
params.require(:tag).permit(:name)
end
end
Routes
Rails.application.routes.draw do
# For details on the DSL available within this file, see
http://guides.rubyonrails.org/routing.html
resources :recipes do
resources :ingredients, :steps
put :favorite, on: :member
end
resources :users
get 'recipes' => 'recipes#index'
get 'recipes/:id' => 'recipes#show'
get 'signup' => 'users#new'
get 'tags' => 'tags#index'
get 'new_tags' => 'tags#new'
post 'tags' => 'tags#create'
delete 'tags' => 'tags#destroy'
get 'login' => 'sessions#new'
post 'login' => 'sessions#create'
delete 'logout' => 'sessions#destroy'
root 'recipes#index'
end
Index
<%= link_to 'New Tag', new_tags_path(#tag) %>
<% Tag.find_each do |tag| %>
<%= tag.name %>
<%= link_to 'Delete Tag', #tag,
method: :destroy,
data: { confirm: 'Are you sure?' } %>
<% end %>
This route:
delete 'tags' => 'tags#destroy'
means "/tags" with no id parameter.
You have to tell the router that you want a part of the url used as the :tag_id
delete 'tags/:tag_id' => 'tags#destroy'
Anyway, I'd recommend you to stick with rails conventions and just use
resources :tags
and on the controller
#tag = Tag.find(params[:id])
I'm not sure why are you setting the routes manually, if you are new to Rails read this link: https://guides.rubyonrails.org/routing.html
You even have this lines
get 'recipes' => 'recipes#index'
get 'recipes/:id' => 'recipes#show'
which are unnecessary since resources :recipes already creates them.
The error stemmed from a combination of issues in the Link To statement in the view. The Link as it should be written is below...
<%= link_to 'Delete Tag', tag_path(tag),
method: :delete,
data: { confirm: 'Are you sure?' } %>
References and External Links
Ruby on Rails - Settting up Reviews functionality
NoMethodError in Discussions#new
http://ruby.about.com/od/rubyonrails/ss/blogpart4_4.htm
Background
I'm implementing a feature in my application that allow users to rate and review pictures.
I am using a Posts/Comments relationship model for a Pictures/Reviews relationship.
Models
class Review < ActiveRecord::Base
belongs_to :picture
end
class Picture < ActiveRecord::Base
has_many :reviews
end
Above, I established a one-to-many relationship between pictures and reviews.
Reviews Migration
class CreateReviews < ActiveRecord::Migration
def change
create_table :reviews do |t|
t.string :username
t.text :body
t.references :picture, index: true
t.timestamps
end
end
end
Matched Routes
match '/pictures/:id/reviews', to: 'reviews#show', via: 'get', :as => 'picture_reviews'
match '/pictures/:id/reviews/edit', to: 'reviews#edit', via: 'get'
match '/pictures/:id/reviews/new', to: 'reviews#new', via: 'get', :as => 'new_reviews'
I will name the route for reviews#edit after I fix this issue with reviews#new.
Error Message
NoMethodError in Reviews#new
Undefined method 'reviews_path' for #<#<Class:0x45c1b00>:0x39ae810>
Extracted source (Around line #8):
5 <div class = 'edit-form'>
6 <div class = 'center'>
7
8 <% form_for #review do |f| %>
9
10 <p>
11 <%= f.label :username %><br />
I checked to see if any files contained 'review-path', but all routes were properly named.
Routes
favorite_picture_path PUT /pictures/:id/favorite(.:format) pictures#favorite
pictures_path GET /pictures(.:format) pictures#index
POST /pictures(.:format) pictures#create
new_picture_path GET /pictures/new(.:format) pictures#new
edit_picture_path GET /pictures/:id/edit(.:format) pictures#edit
picture_path GET /pictures/:id(.:format) pictures#show
PATCH /pictures/:id(.:format) pictures#update
PUT /pictures/:id(.:format) pictures#update
DELETE /pictures/:id(.:format) pictures#destroy
users_path GET /users(.:format) users#index
POST /users(.:format) users#create
new_user_path GET /users/new(.:format) users#new
edit_user_path GET /users/:id/edit(.:format) users#edit
user_path GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
sessions_path POST /sessions(.:format) sessions#create
new_session_path GET /sessions/new(.:format) sessions#new
session_path DELETE /sessions/:id(.:format) sessions#destroy
contacts_path POST /contacts(.:format) contacts#create
new_contact_path GET /contacts/new(.:format) contacts#new
root_path GET / pictures#welcome
users_new_path GET /users/new(.:format) users#new
about_path GET /about(.:format) pictures#about
GET /contacts(.:format) contacts#new
GET /users/:id/favorites(.:format) users#favorites
signup_path GET /signup(.:format) users#new
signin_path GET /signin(.:format) sessions#new
signout_path DELETE /signout(.:format) sessions#destroy
picture_reviews_path GET /pictures/:id/reviews(.:format) reviews#index
GET /pictures/:id/reviews/edit(.:format) reviews#edit
new_reviews_path GET /pictures/:id/reviews/new(.:format) reviews#new
updated_path GET /updated(.:format) pictures#new_updates
GET /top-rated(.:format) pictures#high_ratings
ReviewsController (Part 1)
class ReviewsController < ApplicationController
before_action :set_review, only: [:show, :edit, :update, :destroy]
def index
#picture = Picture.find(params[:id])
#review = Review.all
end
def show
#picture = Picture.find(params[:id])
#review = Review.find(params[:id])
end
def new
#review = Review.new
end
def edit
#picture = Picture.find(params[:picture_id])
#review = Review.find(params[:id])
end
def create
#picture = Picture.find(params[:picture_id])
#review = #picture.reviews.build(params[:review])
if #review.save
flash[:notice] = 'Review was successfully created.'
redirect_to #picture
else
flash[:notice] = "Error creating review: #{#review.errors}"
redirect_to #picture
end
end
Reviews Controller(Part 2)
def update
#picture = Picture.find(params[:picture_id])
#review = Review.find(params[:id])
if #review.update_attributes(params[:review])
flash[:notice] = "Review updated"
redirect_to #picture
else
flash[:error] = "There was an error updating your review"
redirect_to #picture
end
end
def destroy
#picture = Picture.find(params[:picture_id])
#review = Review.find(params[:id])
#review.destroy
redirect_to(#review.post)
end
private
def set_review
#review = Review.find(params[:id])
end
def review_params
params.require(:review).permit(:username, :body, :picture_id)
end
end
Reviews#Index Page
<h3>Reviews for <%= "#{#picture.title}" %></h3>
<table>
<thead>
</thead>
<tbody>
</tbody>
</table>
<div class = 'center'>
<p><%= link_to 'New Review', new_reviews_path(#review), :class => "btn btn-info" %></p>
<p><%= link_to 'Back', picture_path, :class => "btn btn-info" %></p>
</div>
Link to the Reviews#new page
<p><%= link_to 'New Review', new_reviews_path(#review), :class => "btn btn-info" %></p>
Reviews#New Page
<% #title = "New Review" %>
<h3>New Review</h3>
<div class = 'edit-form'>
<div class = 'center'>
<% form_for #review do |f| %>
<p>
<%= f.label :username %><br />
<%= f.text_field :username %>
</p>
<p>
<%= f.label :body %><br />
<%= f.text_area :body %>
</p>
<p>
<%= f.submit "Submit Review" %>
</p>
<% end %>
</div>
</div>
<div class = 'center'>
<%= link_to 'Back', picture_reviews_path(#picture) %>
</div>
Pictures#Show Page
<% #title = "#{#picture.title}" %>
<h4 class = 'indent'>Picture Statistics</h4>
<ul id = 'view'>
<li><strong>Title:</strong> <%= #picture.title %></li>
<li><strong>Category:</strong> <%= #picture.category %></li>
<li><strong>Rating:</strong> <%= pluralize(#picture.rating, 'Star') %></li>
<li><strong>Favorited:</strong> By <%= pluralize(#picture.users.count, 'User') %></li></br>
</ul>
<% if #picture.rating > 4 %>
<button class = 'top-picture'>Top Rated</button>
<% end %>
<%= form_for #picture do |f| %>
<div class = 'indent'>
<p>
<%= f.label :stars, 'Rating' %>
<div class= "rating">
1 ☆<%= f.radio_button :stars, '1' %>
2 ☆<%= f.radio_button :stars, '2' %>
3 ☆<%= f.radio_button :stars, '3' %>
4 ☆<%= f.radio_button :stars, '4' %>
5 ☆<%= f.radio_button :stars, '5' %>
</div>
</p>
<p><input class="btn btn-info" type="submit" value="Rate"></p>
<p><%= link_to 'Reviews', picture_reviews_path(#picture), :class => "btn btn-info" %></p>
<% end %>
<p><%= link_to 'Index', pictures_path, :class => "btn btn-info" %></p>
</div>
I've tried using nested resources like so
resources :pictures do
put :favorite, on: :member
resources :reviews
end
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :contacts, only: [:new, :create]
That didn't work because It routed my pictures using :picture_id instead of the standard :id field. Since it routed to :picture_id it couldn't find any pictures.
picture_reviews_path GET /pictures/:picture_id/reviews(.:format) reviews#index
GET /pictures/:picture_id/reviews/edit/:id(.:format) reviews#edit
new_reviews_path GET /pictures/:picture_id/reviews/new(.:format) reviews#new
Picture Columns
Picture.column_names
=> ['id', 'title', 'category', 'stars', 'created_at', 'updated_at',
'ratings_count', 'ratings_total']
The problem with nesting routes, is that it calls a path using a column_name not found in the table. That is why I decided to go back to matching routes.
I believe the problem lies in my ReviewsController for which there may be duplicated code.
before_action :set_review, only: [:show, :edit, :update, :destroy]
#review = Review.find(params[:id])
def set_review
#review = Review.find(params[:id])
end
I think I could remove the #review = Review.find line from every method, but my main concern is that the set_review method was defined as a private method so that might not be possible.
Help is greatly appreciated and thanks in advanced.
Update
I think the problem lies in my new action in my ReviewsController.
This is just an extended version of #japed answer.
1. You have no route to the create or update action
Both actions works on POST request, hence url_helpers alone won't tell rails what to do with POST request when it gets it. What you need is to change your routes back to nested resources (it was good the way it was, your issue was caused by another bit of code). So, you need:
resources :pictures do
...
resources :reviews
end
Also remove all other routes for this controller as they may affect your final routes. Remeber to restart your server after changing your routes.
2. The controller:
Firstly, note that there are a lot of repetitions there - you are setting #picture in all the actions. Currently your problem is that it is using params[:id] in some actions and params[:picture_id] in others. It should always be picture_id, id should be reserved to be review's id, as you are inside reviews_controller.
The best way to do this is to create another before_filter which will set up the #picture variable:
class ReviewsContorller < ApplicationController
before_filter :set_picture
# This is perfectly fine, but needs to be executed after :set_picture
before_filter :set_review, only: [:show, :edit, :update, :destroy]
...
private
...
def set_picture
#picture = Picture.find(params[:picture_id])
end
def set_review
#review = picture.reviews.find(params[:id])
end
end
Note that the #review is pulled from #picture association - this is important security check, if you used Review.find instead, all the users are automatically able to view, edit and create new reviews for all the photos, without knowing which photo they are really commenting for. It should not be a great issue in your case, but it is good to get this into the habit.
3. The form:
<% form_for #review do |f| %>
This would seems all right, however imagine you are your application - how would you know what is the correct post url for this form? Rails is quite intelligent framework and it is trying to guess it by the resource supplied. In this case, you pass an instance of Review class, hence it will try to send the from to review_path(#review.id). The problem is, that this path does not exists in your routes, so you will get undefined_method 'review_path' here.
Also note, that the proper route you want is /picture/:picture_id/reviews for new reviews or /picture/:picture_id/review/:idfor existing reviews. Hence rails will need the parent picture object to be passed as well to figure out the rightpicture_id`. You can do this by passing an array of resources, with the one which the form is really for being the last so:
<% form_for [#picture, #review] do |f| %>
This will tell rails to look for picture_reviews_path(#picture.id) for new review or picture_review_path(#picture.id, #review.id) for existing reviews. If you have nested resources in your routes, both of those should exists.
4. Other links
Your current routes defines a named path new_reviews which will not longer exist after you use nested resources - it will be renamed to new_picture_review, so you need to change all the occurrences of new_reviews_path to new_picture_review(#picture)
As you're doing nested routes, you need to find by :picture_id as you've just found
class ReviewsController < ApplicationController
before_action { #picture = Picture.find(params[:picture_id] }
end
As your error says the issue is because reviews_path doesn't exist because you've nested it
So this
<% form_for #review do |f| %>
Wants to change to
<% form_for [#picture, #review] do |f| %>
So that it goes to the picture_reviews_path
Also this
<p><%= link_to 'New Review', new_reviews_path(#review), :class => "btn btn-info" %></p>
Wants to become
<p><%= link_to 'New Review', new_picture_reviews_path(#picture, #review), :class => "btn btn-info" %></p>
Can you use Shallow Nesting Routes? That is, you'll have a nested resource where needed, but when unambiguous you get a shorter path, with just one parameter for the review. You can still find your way back to the picture, using the picture_id in the review.
resources :pictures, shallow: true do
put :favorite, on: :member
resources :reviews, shallow: true
end
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :contacts, only: [:new, :create]
Then, improve the models to help the associations to bind well, with inverse_of:
class Review < ActiveRecord::Base
belongs_to :picture, inverse_of: :reviews
end
class Picture < ActiveRecord::Base
has_many :reviews, inverse_of: :picture
end
This should mean there's only one copy of a picture in memory. And then in the ReviewsController:
class ReviewsController < ApplicationController
before_action :set_review, only: [:show, :edit, :update, :destroy]
def index
#picture = Picture.find(params[:id])
# reference #picture.reviews to get all reviews in the view
end
def show
#picture = Picture.find(params[:id])
# use #picture.reviews to get all reviews in the view
end
def new
# where will you get the picture this belongs to?
# Need to collect the picture_id param. and build the associated review
#picture = Picture.find(param[:picture_id])
#review = #picture.reviews.build()
end
def edit
#picture = Picture.find(params[:picture_id])
# use #picture.reviews in the view controller to get the associated reviews
end
def create
#picture = Picture.find(params[:picture_id])
#review = #picture.reviews.build(params[:review])
if #review.save
flash[:notice] = 'Review was successfully created.'
redirect_to #picture
else
flash[:notice] = "Error creating review: #{#review.errors}"
redirect_to #picture
end
end
I think there's one other significant issue. You keep using a piece of code like this:
#review = Picture.find(id)
But that returns zero or more elements. It will help you understand the code better if you reflect that this is, normally, an array:
#reviews = Picture.find(id)
But even better, don't do that. You have the associations. Use them in the view.
#picture.reviews
This will return an array. If zero length, there are no reviews. If non-zero, that's how many review elements there are.
Then you won't make the mistake of picking up an array variable called #review, which appears to be singular (meaning that link_to #review appears to make sense, but will fail), and instead use an array:
<%- #picture.reviews.each do |review| %>
<% link_to review ...%>
Hope that helps!
I have a controller "find_numbers", which I'm using to submit a form to the Twilio API. Before it submits though, I'd like to validate against two form fields, which aren't in the data model for this controller. The fields are :name, and :original_number
So, in my find_numbers model, I added attr_accessor :name, attr_accessor :originial number to run a validates command under it.
After doing that and submitting the form as invalid, I get the error :
Routing Error
No route matches {:controller=>"phone", :action=>"new"}
Try running rake routes for more information on available routes.
I'm not sure why it says there's no roots, but I'm not sure why it's accessing that anyways. I want it to POST to find_numbers
The find_numbers/new template
<%= form_tag("/find_numbers", :method => "post", :id => "new_user" ) do %>
<%= render 'shared/error_messages' %>
<%= label_tag(:name, "What Are You Tracking?") %>
<%= text_field_tag(:name) %>
<%= label_tag(:original_number, "Your Orginal Number") %>
<%= text_field_tag(:original_number) %>
<%= label_tag(:in_postal_code, "Near US postal code (e.g. 94117):") %>
<%= text_field_tag(:in_postal_code) %>
<%= label_tag(:near_number, "Near this other number (e.g. +4156562345)") %>
<%= text_field_tag(:near_number) %>
<%= label_tag(:contains, "Matching this pattern (e.g. 415***EPIC):") %>
<%= text_field_tag(:contains) %>
<%= submit_tag("Search", :class => "btn btn-large btn-primary") %>
<% end %>
here's my find_number model
class FindNumber < ActiveRecord::Base
attr_accessor :name
attr_accessor :original_number
validates :name, presence: true
validates :original_number, presence: true
end
Here's my Find_number controller
class FindNumbersController < ApplicationController
def new
#user = current_user
end
def create
#user = current_user
client = Twilio::REST::Client.new(#user.twilio_account_sid, #user.twilio_auth_token)
search_params = {}
%w[in_postal_code near_number contains].each do |p|
search_params[p] = params[p] unless params[p].nil? || params[p].empty?
end
local_numbers = client.account.available_phone_numbers.get('US').local
#numbers = local_numbers.list(search_params)
unless #numbers.empty?
render 'find_numbers/show'
else
flash.now[:error] = "Sorry, We Couldn't Find Any Numbers That Matched Your Search! Maybe Something Simpler?"
render 'find_numbers/new'
end
end
def show
end
end
Any thoughts on accomplishing this would be greatly appreciated!
Edit
Routes.rb file
Dct::Application.routes.draw do
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :phones, only: [:new, :create, :destroy]
resources :find_numbers, only: [:new, :create, :destroy]
match '/find_numbers', to: 'find_numbers#new'
match '/signup', to: 'users#new'
match '/login', to: 'sessions#new'
match '/signout', to: 'sessions#destroy', via: :delete
root to: 'static_pages#home'
match '/product_demo', to: 'static_pages#product_demo'
match '/pricing', to: 'plans#index'
match '/contact', to: 'static_pages#contact'
Edit
Here is the server log, of what happened when I hit submit
http://stepanp.com/railserror.jpg
Also, here's the find_numbers/show view
From what you've posted, the only other thing that looks suspicious to me is that you presumably have a PhonesController (plural) since you've declared resources :phones, but the routing error seems to occur because it is looking for a PhoneController (singular).