I'm looking to display all posts from a certain category on a page, I currently have Posts & Category models linked by a HABTM relationship. I want to be able to click the link on my index.html.erb and go through to a page that lists all the posts which belong to that certain category.
Do I need to create a controller for category and new routes?
Post.rb
class Post < ActiveRecord::Base
has_and_belongs_to_many :categories
belongs_to :user
end
Category.rb
class Category < ActiveRecord::Base
has_and_belongs_to_many :posts
end
Index.html.erb (My current way to display the category for each post)
<% post.categories.each do |category| %>
<% category.posts.each do |post| %>
<%= link_to category.name, post_url(post) %>
<% end %>
<% end %>
** Update **
After Answer routes have been produced as below.
category_posts GET /categories/:category_id/posts(.:format) posts#index
POST /categories/:category_id/posts(.:format) posts#create
new_category_post GET /categories/:category_id/posts/new(.:format) posts#new
edit_category_post GET /categories/:category_id/posts/:id/edit(.:format) posts#edit
category_post GET /categories/:category_id/posts/:id(.:format) posts#show
PATCH /categories/:category_id/posts/:id(.:format) posts#update
PUT /categories/:category_id/posts/:id(.:format) posts#update
DELETE /categories/:category_id/posts/:id(.:format) posts#destroy
categories GET /categories(.:format) categories#index
POST /categories(.:format) categories#create
new_category GET /categories/new(.:format) categories#new
edit_category GET /categories/:id/edit(.:format) categories#edit
category GET /categories/:id(.:format) categories#show
PATCH /categories/:id(.:format) categories#update
PUT /categories/:id(.:format) categories#update
DELETE /categories/:id(.:format) categories#destroy
Have to do it as answer:
In your Category model:
def to_param
name
end
But please accept Logan's anwer as the correct one, because he solves your original problem.
You can nest posts inside categories
resources :categories do
resources :posts
end
This will make routes like categories/1/posts and URL helpers like category_posts_path(1) where 1 is the category ID. Then in your posts controller, you will have params[:category_id] available which can lookup the category and then fetch the posts. Something like
if params[:category_id]
#category = Category.find params[:category_id]
#posts = #category.posts
else
#posts = Post.all
end
def index
if params[:category_id]
#posts = Post.where("category_id = ?", params[:category_id])
else
#posts = Post.all
end
end
An alternative to Logan's Answer as in some cases it might not work.
Related
I submitted this question yesterday and was mostly able to get the issue solved from the answers I got there, however I'm now having a different but related problem.
I have this code in my show view for my object, Car:
<%= link_to #user do %>
<%= image_tag #user.profile.avatar.url, class: 'user-index-avatar' %>
<h3><%= #user.profile.company %></h3>
<% end %>
<h4><%= #user.profile.location %></h4>
<h4><%= #user.profile.phone_number %></h4>
<h4><%= #user.profile.email %></h4>
<% if user_signed_in? && #user.id == current_user.id %>
<%= link_to "Edit Listing", edit_car_path(id: #car.id ), class: 'btn btn-lg btn-warning btn-block' %>
<% end %>
When I go to this page, I get the error message "ActiveRecord::RecordNotFound in CarsController#show" and "Couldn't find User with 'id'="
Rake routes:
cars GET /cars(.:format) cars#index
GET /cars(.:format) cars#index
POST /cars(.:format) cars#create
new_car GET /cars/new(.:format) cars#new
edit_car GET /cars/:id/edit(.:format) cars#edit
car GET /cars/:id(.:format) cars#show
PATCH /cars/:id(.:format) cars#update
PUT /cars/:id(.:format) cars#update
DELETE /cars/:id(.:format) cars#destroy
new_user_profile GET /users/:user_id/profile/new(.:format) profiles#new
edit_user_profile GET /users/:user_id/profile/edit(.:format) profiles#edit
user_profile GET /users/:user_id/profile(.:format) profiles#show
PATCH /users/:user_id/profile(.:format) profiles#update
PUT /users/:user_id/profile(.:format) profiles#update
DELETE /users/:user_id/profile(.:format) profiles#destroy
POST /users/:user_id/profile(.:format) profiles#create
user_cars GET /users/:user_id/cars(.:format) cars#index
POST /users/:user_id/cars(.:format) cars#create
new_user_car GET /users/:user_id/cars/new(.:format) cars#new
GET /cars/:id/edit(.:format) cars#edit
GET /cars/:id(.:format) cars#show
PATCH /cars/:id(.:format) cars#update
PUT /cars/:id(.:format) cars#update
DELETE /cars/:id(.:format) cars#destroy
Models:
class Car < ActiveRecord::Base
belongs_to :user
end
class User < ApplicationRecord
belongs_to :plan
has_one :profile
has_many :cars
Routes.rb
get 'cars' => 'cars#index', as: :cars
resources :cars
resources :users do
resource :profile
resources :cars, shallow: true
end
CarsController:
def show
#car = Car.find( params[:id] )
#user = User.find( params[:user_id] )
#profile = #user.profile
end
The error specifies the issue is with "#user = User.find( params[:user_id] )", but I'm not sure how else to define #user. As mentioned in the linked problem above, I'm a complete newbie, so please forgive me if this is an obvious fix.
Rails find method raises this exception when it is unable to find a record by a specified id. You can double check your user_id in params and see if that id's record actually exists in database.
And by the error you have pasted, it seems user_id is null in your params.
You can always do some thing like
puts "Look for me in console\n"*10
puts params.inspect
And than see in the log window what params you are actually receiving.
I see your problem is solved in comments by accessing user association in car model, but this answer is intended to point to the root of problem and decrease number of unanswered question in SO :)
This is my first rails project and I'm a complete newbie to all code, not just Ruby, so please forgive me if I don't explain my issue concisely.
I have a model, Car, which belongs to another model, User. I want my Cars index page to show all cars in the database, so I made a custom route of '/cars/', rather than the :user_id/cars/:id route generated by Rails.
The Car info is getting output onto my index page, but I can't work out how to generate links back to the Car show page.
I'm sure it's something simple, but I've been reading the Rails Guide and other questions on here all day and haven't fixed it yet.
Here's the block:
<% #cars.each do |car| %>
<li>
<div class="well row <%= cycle('', 'white-bg') %>">
<div class="col-sm-4 text-center">
<!-- thumbnail for car here -->
</div>
<div class="pull-left">
<%= link_to car.id, user_car_path(#user, car) do %>
<h3><%= car.year %> <%=car.make %> <%= car.model %></h3>
<% end %>
</div>
<div>
<h3 class="pull-right"><%= car.price %></h3>
</div>
</div>
</li>
<% end %>
Routes:
get 'cars' => 'cars#index', as: :cars
resources :users do
resource :profile
resources :cars, except: :index
end
end
Controller:
def new
#user = User.find( params[:user_id] )
#car = #user.cars.build
end
# POST request to /users/:user_id/cars
def create
#user = User.find( params[:user_id] )
#car = #user.cars.build( car_params )
if #car.save
redirect_to user_path( params[:user_id] )
end
end
# GET request to /users/:user_id/cars/:id
def show
#user = User.find( params[:user_id] )
#car = #user.cars.find( params[:id] )
end
# GET request to /cars/
def index
#cars = Car.all
end
The error is:
No route matches {:action=>"show", :controller=>"cars", :id=>"1", :user_id=>nil} missing required keys: [:user_id]
I'm guessing I'm missing something in the controller, but everything I've tried in there just generates other errors.
Thanks!
cars_path GET /cars(.:format) cars#index
POST /cars(.:format) cars#create
new_car_path GET /cars/new(.:format) cars#new
edit_car_path GET /cars/:id/edit(.:format) cars#edit
car_path GET /cars/:id(.:format) cars#show
PATCH /cars/:id(.:format) cars#update
PUT /cars/:id(.:format) cars#update
DELETE /cars/:id(.:format) cars#destroy
new_user_profile_path GET /users/:user_id/profile/new(.:format)
profiles#new
edit_user_profile_path GET /users/:user_id/profile/edit(.:format)
profiles#edit
user_profile_path GET /users/:user_id/profile(.:format)
profiles#show
PATCH /users/:user_id/profile(.:format) profiles#update
PUT /users/:user_id/profile(.:format) profiles#update
DELETE /users/:user_id/profile(.:format) profiles#destroy
POST /users/:user_id/profile(.:format) profiles#create
user_cars_path GET /users/:user_id/cars(.:format) cars#index
POST /users/:user_id/cars(.:format) cars#create
new_user_car_path GET /users/:user_id/cars/new(.:format) cars#new
GET /cars/:id/edit(.:format) cars#edit
GET /cars/:id(.:format) cars#show
PATCH /cars/:id(.:format) cars#update
PUT /cars/:id(.:format) cars#update
DELETE /cars/:id(.:format) cars#destroy
I would suggest an alternative solution where you "unnest" the cars resource - and just provide a index route for the cars belonging to a certain user:
# routes.rb
resources :cars
resources :users do
resources :cars, module: 'users', only: [:index]
end
# app/controller/cars_controller.rb
class CarsController < ApplicationController
# GET /cars
def index
#cars = Car.all
end
# show, create, delete, new...
end
# app/controller/users/cars_controller.rb
class Users::CarsController < ApplicationController
# GET /users/:user_id/cars
def index
#user = User.includes(:cars).find(params[:user_id])
#cars = #user.cars
end
end
Depending on the context you can move more of the collection routes (new, create) to Users::CarsController if you for example are able to create cars for other users. Nesting member routes (that act on a single record)
is seldom necessary. You can avoid it by using the shallow: true option:
resources :users do
resources :cars, shallow: true
end
This lets you route to a car by simply doing:
link_to(#car.name, car_path(#car))
# or
link_to(#car.name, #car)
If you decide to keep your current setup you route to nested resource by using an array or keywords:
link_to(#car.name, user_car_path(user: #user, id: #car))
# or
link_to(#car.name, [#user, #car])
I am having index method to list all students. Now I am adding search in the index with one text_field_tag. My function is working but the search value is showing as query string in the url. I don't want this.
My view code:
<p>
<%= form_tag students_path, :method => 'get' do %>
Find by name: <%= text_field_tag :search %>
<%= submit_tag "Search", :name => nil %>
<% end %>
</p>
My controller code is
def index
searchString = params[:search]
if searchString
#students = Student.where("LastName like ?", "%#{searchString}%").paginate(:page => params[:page])
else
#students = Student.paginate(:page => params[:page])
end
end
My current url look like below:
http://localhost:3000/students?utf8=%E2%9C%93&search=A
I just need like /students. Please suggest the best practice
You need to use POST request instead of GET. In order to accomplish that, change your form_tag from:
<%= form_tag students_path, :method => 'get' do %>
to
<%= form_tag students_path, :method => 'post' do %>
What could possibly go wrong...
As #carlosramireziii mentioned, answering this question, requires a bit more work (and he is absolutely right!), so - there is update
1. Custom routes
If you are configuring your routes on your own, one of ways proceeding with this is to add a route, that will properly accept your requests. Take a look at config/routes.rb:
Rails.application.routes.draw do
match '/students', controller: :students, action: :index, via: [:get, :post]
# other definitions
end
Definition like this will create following route for you:
students GET|POST /students(.:format) students#index
This will make your new POST request nicely fall into students#index, generating expected result. However, if you are defining your routes via resources it's not that easy change. This leads us to...
2. Modifying standard resources routes
If routes for students are defined as follows:
Rails.application.routes.draw do
resources :students
# other definitions
end
the following routes are registered:
students GET /students(.:format) students#index
POST /students(.:format) students#create
new_student GET /students/new(.:format) students#new
edit_student GET /students/:id/edit(.:format) students#edit
student GET /students/:id(.:format) students#show
PATCH /students/:id(.:format) students#update
PUT /students/:id(.:format) students#update
DELETE /students/:id(.:format) students#destroy
This indicates, the POST request falls into students#create. In order to make it work, you need to change the create action, to something like:
class StudentsController < ApplicationController
# code omitted
def create
if params.include?('search')
#students = Student.where("LastName like ?", "%#{searchString}%").paginate(:page => params[:page])
render :index
else
# your current code from create action
end
end
end
While this will work, it is not recommended to do it this way. The create action does two things now, which is considered a bad pattern (or bad smell at least), and it is error prone.
So, what can we do?
Making search the right way
1. Custom search route
Define custom collection route:
Rails.application.routes.draw do
resources :students do
collection do
post :search
end
end
# other definitions
end
This registers following routes:
search_students POST /students/search(.:format) students#search
students GET /students(.:format) students#index
POST /students(.:format) students#create
new_student GET /students/new(.:format) students#new
edit_student GET /students/:id/edit(.:format) students#edit
student GET /students/:id(.:format) students#show
PATCH /students/:id(.:format) students#update
PUT /students/:id(.:format) students#update
DELETE /students/:id(.:format) students#destroy
You need to change your form_tag as follows:
<%= form_tag search_students_path, :method => 'post' do %>
And add search action to StudentsController:
class StudentsController < ApplicationController
# code omitted
def search
#students = Student.where("LastName like ?", "%#{searchString}%").paginate(:page => params[:page])
# If you don't want to create separate template for `search`,
# you can try to reuse your `index` template with
# render :index
end
end
2. Use students#index with params passed in URL
This approach is more REST-y, and it makes it available to pass the whole URL to someone else, so the another person is able to see exactly the same result of filtering, which is impossible with params "hidden" in POST request.
I hope I've covered all possibilities. If you have any questions - I'm more than happy to answer!
Good luck!
I have a has_many :through association between model Thing and Category, so each one has_many of the other. It works when I create a new Thing and associate it with a Category, and then put Category.name in the Thing view, but then I try to put it as a link, it returns the error "ActionController::UrlGenerationError in Things#show". What am I doing wrong?
Thing Controller
class ThingsController < ApplicationController
def show
#thing = Thing.find(params[:id])
#category_things = CategoryThing.all
#thing.categories.build
end
def index
end
def new
#thing = Thing.new
#things = Thing.all
end
def create
#thing = Thing.new(thing_params)
if #thing.save
redirect_to #thing
else
render 'new'
end
end
private
def thing_params
params.require(:thing).permit(:name, :image_path, :avatar)
end
def results
end
end
Category Controller
class CategoriesController < ApplicationController
def show
#category = Category.find(params[:id])
end
def new
#category = Category.new
end
def create
#category = Category.new(category_params)
if #category.save
redirect_to #category
else
render 'new'
end
end
private
def category_params
params.require(:category).permit(:name)
end
def results
end
end
Thing Model
class Thing < ActiveRecord::Base
has_many :category_things
has_many :categories, :through => :category_things
end
Category Model
class Category < ActiveRecord::Base
has_many :category_things
has_many :things, :through => :category_things
end
CategoryThing Model
class CategoryThing < ActiveRecord::Base
belongs_to :category
belongs_to :thing
end
Thing.show View
<div id= "thing">
<p>
<% #thing.categories.each do |category| %>
<%= link_to category.name, category_path(category) %>
*****[THE ABOVE WORKS WHEN I JUST PUT "category.name"]*****
<% end %>
</p>
</div>
Thing.new View
<h1>Add Something!</h1>
<p>
<%= form_for #thing, :url => things_path do |f| %>
<%= f.text_field :name, :placeholder => "Name of the thing" %>
<br>
<%= f.label :categories_the_thing_belongs_to %>
<%= f.collection_select :categories, Category.all, :id, :name %>
<br>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
</p>
Output of rake routes
Prefix Verb URI Pattern Controller#Action
ratings_new GET /ratings/new(.:format) ratings#new
down_votes_new GET /down_votes/new(.:format) down_votes#new
thing_new GET /thing/new(.:format) thing#new
good_comments_new GET /good_comments/new(.:format) good_comments#new
good_comments_show GET /good_comments/show(.:format) good_comments#show
bad_comments_new GET /bad_comments/new(.:format) bad_comments#new
related_things_new GET /related_things/new(.:format) related_things#new
things_new GET /things/new(.:format) things#new
category_things_new GET /category_things/new(.:format) category_things#new
thing_ratings_new GET /thing_ratings/new(.:format) thing_ratings#new
category_ratings_new GET /category_ratings/new(.:format) category_ratings#new
subjects GET /subjects(.:format) subjects#index
POST /subjects(.:format) subjects#create
new_subject GET /subjects/new(.:format) subjects#new
edit_subject GET /subjects/:id/edit(.:format) subjects#edit
subject GET /subjects/:id(.:format) subjects#show
PATCH /subjects/:id(.:format) subjects#update
PUT /subjects/:id(.:format) subjects#update
DELETE /subjects/:id(.:format) subjects#destroy
subjects_show GET /subjects/show(.:format) subjects#show
subject_things_new GET /subject_things/new(.:format) subject_things#new
categories GET /categories(.:format) categories#index
POST /categories(.:format) categories#create
new_category GET /categories/new(.:format) categories#new
edit_category GET /categories/:id/edit(.:format) categories#edit
category GET /categories/:id(.:format) categories#show
PATCH /categories/:id(.:format) categories#update
PUT /categories/:id(.:format) categories#update
DELETE /categories/:id(.:format) categories#destroy
categories_results GET /categories/results(.:format) categories#results
subjects_new GET /subjects/new(.:format) subjects#new
root GET / home_page#home
all_things_new GET /all/things/new(.:format) things#new
all_allthings GET /all/allthings(.:format) all#allthings
thing_good_comments GET /things/:thing_id/good_comments(.:format) good_comments#index
POST /things/:thing_id/good_comments(.:format) good_comments#create
new_thing_good_comment GET /things/:thing_id/good_comments/new(.:format) good_comments#new
edit_thing_good_comment GET /things/:thing_id/good_comments/:id/edit(.:format) good_comments#edit
thing_good_comment GET /things/:thing_id/good_comments/:id(.:format) good_comments#show
PATCH /things/:thing_id/good_comments/:id(.:format) good_comments#update
PUT /things/:thing_id/good_comments/:id(.:format) good_comments#update
DELETE /things/:thing_id/good_comments/:id(.:format) good_comments#destroy
thing_bad_comments GET /things/:thing_id/bad_comments(.:format) bad_comments#index
POST /things/:thing_id/bad_comments(.:format) bad_comments#create
new_thing_bad_comment GET /things/:thing_id/bad_comments/new(.:format) bad_comments#new
edit_thing_bad_comment GET /things/:thing_id/bad_comments/:id/edit(.:format) bad_comments#edit
thing_bad_comment GET /things/:thing_id/bad_comments/:id(.:format) bad_comments#show
PATCH /things/:thing_id/bad_comments/:id(.:format) bad_comments#update
PUT /things/:thing_id/bad_comments/:id(.:format) bad_comments#update
DELETE /things/:thing_id/bad_comments/:id(.:format) bad_comments#destroy
thing_ratings GET /things/:thing_id/ratings(.:format) ratings#index
POST /things/:thing_id/ratings(.:format) ratings#create
new_thing_rating GET /things/:thing_id/ratings/new(.:format) ratings#new
edit_thing_rating GET /things/:thing_id/ratings/:id/edit(.:format) ratings#edit
thing_rating GET /things/:thing_id/ratings/:id(.:format) ratings#show
PATCH /things/:thing_id/ratings/:id(.:format) ratings#update
PUT /things/:thing_id/ratings/:id(.:format) ratings#update
DELETE /things/:thing_id/ratings/:id(.:format) ratings#destroy
thing_up_votes GET /things/:thing_id/up_votes(.:format) up_votes#index
POST /things/:thing_id/up_votes(.:format) up_votes#create
new_thing_up_vote GET /things/:thing_id/up_votes/new(.:format) up_votes#new
edit_thing_up_vote GET /things/:thing_id/up_votes/:id/edit(.:format) up_votes#edit
thing_up_vote GET /things/:thing_id/up_votes/:id(.:format) up_votes#show
PATCH /things/:thing_id/up_votes/:id(.:format) up_votes#update
PUT /things/:thing_id/up_votes/:id(.:format) up_votes#update
DELETE /things/:thing_id/up_votes/:id(.:format) up_votes#destroy
things GET /things(.:format) things#index
POST /things(.:format) things#create
new_thing GET /things/new(.:format) things#new
edit_thing GET /things/:id/edit(.:format) things#edit
thing GET /things/:id(.:format) things#show
PATCH /things/:id(.:format) things#update
PUT /things/:id(.:format) things#update
DELETE /things/:id(.:format) things#destroy
things_show GET /things/show(.:format) things#show
things_results GET /things/results(.:format) things#results
things_random GET /things/random(.:format) things#random
web_console /console WebConsole::Engine
Routes for WebConsole::Engine:
root GET / web_console/console_sessions#index
input_console_session PUT /console_sessions/:id/input(.:format) web_console/console_sessions#input
pending_output_console_session GET /console_sessions/:id/pending_output(.:format) web_console/console_sessions#pending_output
configuration_console_session PUT /console_sessions/:id/configuration(.:format) web_console/console_sessions#configuration
console_sessions GET /console_sessions(.:format) web_console/console_sessions#index
POST /console_sessions(.:format) web_console/console_sessions#create
new_console_session GET /console_sessions/new(.:format) web_console/console_sessions#new
edit_console_session GET /console_sessions/:id/edit(.:format) web_console/console_sessions#edit
console_session GET /console_sessions/:id(.:format) web_console/console_sessions#show
PATCH /console_sessions/:id(.:format) web_console/console_sessions#update
PUT /console_sessions/:id(.:format) web_console/console_sessions#update
DELETE /console_sessions/:id(.:format) web_console/console_sessions#destroy
Routes.rb
Website::Application.routes.draw do
get "ratings/new"
get "down_votes/new"
get "thing/new"
get "good_comments/new"
get "good_comments/show"
get "bad_comments/new"
get "related_things/new"
get "things/new"
get "category_things/new"
get "thing_ratings/new"
get "category_ratings/new"
resources :subjects
get "subjects/show"
get "subject_things/new"
resources :categories
get "categories/results"
get "subjects/new"
root 'home_page#home'
get "all/things/new" => 'things#new'
get "all/allthings"
resources :things do
resources :good_comments
resources :bad_comments
resources :ratings
resources :up_votes
end
get "things/show"
get "things/results"
get 'things/random' => 'things#random'
end
Previous i used:
data: {autocomplete_source: categories_path} %>
To point to the action index in categories controller.All worked fine!
Now i created an new action in categories controller
def search
#categories = Category.order(:name).where("name like ?", "%#{params[:term]}%")
render json: #categories.map(&:name)
end
And tried to point to that action:
data: {autocomplete_source: search_categories_path} %>
But i get the error:
undefined local variable or method `search_categories_path' for #<#<Class:0x51844c8>:0x5375820>
What did i wrong? Thanks!
My routes:
products GET /products(.:format) products#index
POST /products(.:format) products#create
new_product GET /products/new(.:format) products#new
edit_product GET /products/:id/edit(.:format) products#edit
product GET /products/:id(.:format) products#show
PUT /products/:id(.:format) products#update
DELETE /products/:id(.:format) products#destroy
categories GET /categories(.:format) categories#index
POST /categories(.:format) categories#create
new_category GET /categories/new(.:format) categories#new
edit_category GET /categories/:id/edit(.:format) categories#edit
category GET /categories/:id(.:format) categories#show
PUT /categories/:id(.:format) categories#update
DELETE /categories/:id(.:format) categories#destroy
Routes:
Autorails::Application.routes.draw do
resources :products
resources :categories do
collection do
:search
end
end
Check rake routes if that route really exists under that name.
For more information see http://guides.rubyonrails.org/routing.html#path-and-url-helpers
You should have something like this in your routes.rb:
resources :categories do
collection do
get :search
end
end
You should do something like this
resources :categories do
collection do
get :search
end
end