The problem is i want to have both nested resources and normal resources pointing the the same action in controller but act differently based on whether the nesting resource in available or not.
Routes.rb
resources :users do
resources :comments //For having nested routes
end
resources :comments //For having normal routes
Nested Resource
For example
Class CommentsController < ApplicationController
def index
#user = User.find(params[:id])
#comment = #user.comments.find(params[:id])
end
Now,
If i want to just look up all the comments , i want to use the
/comments
Else if i am in the user page and click on the all comments i get to
/user/:user_id/comments
Hence, how to configure to make sure it generates the right page.
If I were you, I would do like this:
def index
#comment = Comment.find_by(user_id: params[:user_id], id: params[:id])
# It looked weird that you tried to find a #comment
# instead of #comments in action `index`
# I think you made a typing mistake and this can be help
#comments = Comment.where(user_id: params[:user_id])
end
Related
resources :recipes do
resource :like, module: :recipes
resources :comments, only: [:new, :create, :show, :index], module: :recipes
end
recipe_comments GET /recipes/:recipe_id/comments(.:format) recipes/comments#index
POST /recipes/:recipe_id/comments(.:format) recipes/comments#create
Comments are in /recipes/:id
Recipe Controller
def show
#recipe = Recipe.find(params[:id])
#comment = #recipe.comments.new
#clean_recipe = Sanitizer.new(#recipe)
end
Recipes::CommentsController
Theres a before action that finds recipe.
def create
#comment = #recipe.comments.new(comment_params)
#comment.user_id = current_user.id
if #comment.save
redirect_to recipes_path
end
end
I've done
form_for([#article,#comment]) and form_for [#article,Comment.new]
and still the comment isn't persisted. I am wondering because of the module namespace, do I have to do something different?
Add logger.info 'I am inside create action in comments controller' inside the create action. If you see this logger message in the rails console, then the create action gets called. It means your routes are defined correctly, your form is using the right form helper.
the correct format is form_for([#recipe, #comment]) however, I had a length validation in my comment model which was failing to pass. I set too_short and too_long messages, however they didn't show up, which further confused me
I have several books that users can create and then create pages for them.
routes.rb:
resources :books do
member do
get 'pages'
end
end
This gives a link to create a new page through /books/:book_id/pages
In that page I render a partial:
<%= render '/pages/new' %>
book controller:
def page
#book = Book.find(params[:id])
#page = #book.pages.new
end
But when params are passed to a page controller the book_id is lost:
pages controller:
def create
#page = Page.new(params[:page])
if #page.save
redirect_to #page
else
render 'new'
end
end
Also error handling becomes difficult as after the redirect the id also disappears.
I believe rest of the application seems fine as when I create the page through console the book_id is preserved.
It is my first app so I'm not even sure if this is even the right way to approach it... How can I get this to work?
Thanks guys!
Nested resources can be handled in such a way.
# config/routes.rb
resources :books do
resources :pages
end
You can also restrict the actions being created by using only: [:index, :new] or except: [:destroy] on the resources call.
In your PagesController you would have the following params available.
GET /books/:book_id/pages/:id => creates a params[:book_id] and params[:id]
# app/controllers/pages_controller.rb
def create
#book = Book.find(params[:book_id])
#page = #book.pages.build(params[:page])
if #page.save
# ...
else
# ..
end
end
Note that the method call for building a new object through the association is #book.pages.build instead of .new. You'll find out all about Rails associations and how to build and create the objects through association in the Rails Guides on ActiveRecord associations. Hope these pointers help.
If you want to debug incoming parameters, you can just raise them as C404 suggested above.
# in any controller
def any_method
raise params.inspect
# or in yaml format
raise params.to_yaml
end
One way to debug this is to use "puts" in your controller for params to see what actually exists. "puts" are like println's in java or echo's in PHP
Please post the contents of params when ran with this code
def create
puts "params = #{params}"
#page = Page.new(params[:page])
if #page.save
redirect_to #page
else
render 'new'
end
end
For example: can I render '/tags/:id/posts'?
If I try to render tag_posts_path(#tag) or some other ways, I get this or similar error:
Missing partial /tags/1/posts...
In my routes.rb I have this:
resources :tags do
resources :posts
end
I've stucked a bit with thing like yours
I achieved rendering correct page
def create
#project = Project.find(params[:project_id])
#comment = #project.comments.build(comment_params)
if #comment.save
flash[:success] = "Chingon!"
redirect_to #project
else
render 'projects/show'
end
end
My routes looks like:
resources :projects do
resources :comments, only: [:create, :destroy]
end
Rails expecting you to render not path but some page(like 'new' or 'show')
I got trouble only with my ELSE where I'm rendering 'projects/show'. It seem to be not quite right template, coz it renders in raw, without any CSS but with errors I expect to appear. It's the only way for now that I can render error messages for empty comment.
I walk over that issue simply by bypassing default way to display errors and place it in simple flash like this:
def create
#project = Project.find(params[:project_id])
#comment = #project.comments.build(comment_params)
if #comment.save
flash[:success] = "New comment here, bro"
redirect_to #project
else
redirect_to #project
flash[:error] = flash_error_message(#comment)
end
end
def flash_error_message(arg)
"The form contains #{arg.errors.count} error: #{arg.errors.full_messages.join(', ')}"
end
If I'm correct, I think what you are looking for is something like this
resources :tags do
resources :posts
end
You can see what kind of urls or routes this generates by typing in rake routes
I'm not really sure but, re-reading your question I think you mean rendering by saying
render 'some nested route'
The above routes I provided would allow you to do something like this...
render tag_posts_path(#tag) that will in term look for a index action since its the posts path. Again, rake routes shows all this.
A quick search on google for nested routes would've led you to this.
Nested Routes
I'm working on a blog like application,
my user module has_many posts and the posts module belongs_to user
I want to access both users/:id/posts and posts/
routes.rb is something like this:
resources :users do
resources :posts
end
resources:posts
how can i know within the posts controller if its accessed directly (/posts) or through the nested route (/users/:id/posts) ?
for example, what should be the index method of the posts controller for doing the correct INDEX action for /users/:id/posts and for /posts
is there a better way for doing this ?
One solution could be to use a before filter on your controller, like:
before_filter :load_user
def load_user
#user = User.find(params[:user_id]) if params[:user_id]
#posts = #user ? #user.posts : Post.all
end
Then you have to rewrite your controller a bit to function properly.
No refactoring needed on index action, #posts already loaded correctly, but you can do further filtering as you like
def index
#posts = #posts.where('updated_at < ?' Time.now)
end
Then update every member action: new, create, show, edit, update, destroy and use posts as a base like:
def new
#post = #posts.build
end
def create
#post = #posts.build(params[:task])
end
def show
#post = #posts.find(params[:id])
end
def edit
#post = #posts.find(params[:id])
end
def update
#post = #posts.find(params[:id])
end
def destroy
#post = #posts.find(params[:id])
end
Of course you can add other before filters to remove duplicate code.
Check the params.
If just post you'll just have :id
If user/post you'll have user and ID for post.
So check if params[:user]...
n.b. If not user, try params[:user_id]
As for the index method for posts I think it will actually be the SAME in both cases. What will change things is its usage, association and scoping within user.
I have a scenario being:
resources :magazines do
resources :articles do
resources :comments
end
end
So as to avoid nesting more than 2 levels deep I have re-factored this to be:
resources :magazines do
resources :articles
end
resources :articles do
resources :comments
end
My article show action URL is:
/magazines/3/articles/11
In this view I have a form for creating a new comment.
When a comment is saved successfully the form redirects which all works well.
When the form submission is not successful I wish to redisplay the view with validations errors displayed. I understand the correct way to do this is to render the 'articles/show' view. This also works and the view is redisplayed with the validation errors shown.
The problem is when the save fails and articles/show is rendered the URL is no longer correct and is shown as:
/articles/11/comments
class ArticlesController < ApplicationController
def show
#article = Article.find(params[:id])
#comments = #article.comments.order(created_at: :asc).page(params[:page]).per_page(5)
#comment = Comment.new
end
end
class CommentsController < ApplicationController
def create
#article = Article.find(params[:id])
#comment = #article.comments.new(discussion_params)
#comment.user_id = current_user.id
if #comment.save
redirect_to #article
else
render 'articles/show'
end
end
private
def discussion_params
params.require(:comment).permit(:content)
end
end
I solved this by changing my routes back to the way it originally was and now the article show action includes the magazine in the url.
I understand this breaks the "no more than 2 levels deep" routing rule but it's the only way I can get it to work.