Nested resources in view page. (3 levels) - sql

I believe we are all familiar with the normal posts and comments models to be displayed. Now imagine those relations hold. I would like to add replies to my comments so we have routing as follows
resources :posts do
resources :comments do
resources :replies do
end
end
I tried many different ways of implementing this in my views but I had no luck! It just does not work when my actual controller is post and I want to access replies. I tried searching for this but did not find the suitable name for it. Is there any resource that have information about how to implement this system or a code snippet on how to make it work on n levels instead of just 2?

http://nithinbekal.com/posts/rails-shallow-nesting/
You can use shallow nesting instead.
resources :posts, shallow: true do
resources :comments do
resources :replies do
end
end
This way you only have a nested route when you need to create a nested object [new and create action] or see all the related objects [index view]. Look at the first three lines below to see what i mean. And then you have normal routes for the resources that dont need to know the relation.
comment_replies GET /comments/:comment_id/replies(.:format) replies#index
POST /comments/:comment_id/replies(.:format) replies#create
new_comment_reply GET /comments/:comment_id/replies/new(.:format) replies#new
edit_reply GET /replies/:id/edit(.:format) replies#edit
reply GET /replies/:id(.:format) replies#show
PATCH /replies/:id(.:format) replies#update
PUT /replies/:id(.:format) replies#update
DELETE /replies/:id(.:format) replies#destroy
post_comments GET /posts/:post_id/comments(.:format) comments#index
POST /posts/:post_id/comments(.:format) comments#create
new_post_comment GET /posts/:post_id/comments/new(.:format) comments#new
edit_comment GET /comments/:id/edit(.:format) comments#edit
comment GET /comments/:id(.:format) comments#show
PATCH /comments/:id(.:format) comments#update
PUT /comments/:id(.:format) comments#update
DELETE /comments/:id(.:format) comments#destroy
posts GET /posts(.:format) posts#index
POST /posts(.:format) posts#create
new_post GET /posts/new(.:format) posts#new
edit_post GET /posts/:id/edit(.:format) posts#edit
post GET /posts/:id(.:format) posts#show
PATCH /posts/:id(.:format) posts#update
PUT /posts/:id(.:format) posts#update
DELETE /posts/:id(.:format)

Rule of thumb:
Resources should never be nested more than 1 level deep.
I suggest you to read this article by Jamis Buck. You probably want to do something like:
resources :posts do
resources :comments
end
resources :comments
resources :replies
end

Related

Rails 3 Routes and Namespaces

I have recently inherited an app where a user's edit account methods along with public account methods all live in the same controller. I am looking to try to make things a little more "Rails-esque" to clean the app up.
Here are the current routes
resources :user, :only => [:update, :show] do
member do
get :edit_account
put :edit_account
put :update_billing_information
put :update_account_type
get :edit_vitals
get :edit_videos
get :edit_athletic
get :edit_academic
get :edit_social
get :edit_news
get :edit_contributors
get :edit_primary_sport_positions
get :edit_primary_sport
get :edit_primary_sport_highlights
delete :remove_alternate_position
get :vitals
get :videos
get :athletic
get :academic
get :social
get :news
post :keep
post :draft
get :share
post :share
get :notes
post :notes
get :contact
post :contact
post :cut
delete :deactivate_account
delete :delete_authorization
end
get :change_current_sport, on: :collection
end
I reckon what I am looking for is, what would be the best rails best practices to move this towards routes that resemble /user/edit/account or /user/edit/billing?

Are deeply nested polymorphic resources worth the effort?

I am at the point in my development that I am thinking that deeply (>1) nested resources are not worth the effort.
I have something like this:
resources :first-level do
resources :comments
resources :second-level do
resources :comments
resources :first-child do
resources :comments
end
resources :second-child do
resources :comments
end
resources :third-child do
resources :comments
end
end
end
The kicker is that the comments are polymorphic to the other resources. My intent was to have clean looking URLs like ~/first-level/34/comments, ~/first-level/34/second-level/56/third-level/comments etc.
The problem so far is the polymorphic routes when nested cause nothing but grief. I am following a couple of Ryan Bates Railscasts as an example. For example if I try to use polymorphic_path on the first level it works fine and I get:
polymorphic_path([#commentable, comments]) => ~/first-level/34/comments
but the same code on ~/first-level/34/second-level/23 fails with:
undefined method 'second-level_comment_path' for #<#<Class:0x007fcc4acfbe58>:0x007fcc4ae73d08> but when I look at my routes the actual named route is first-level_second-level_comment. I tried to manually create that second-level_comment_path to basically alias to first-level_second-level_comment but I could not seem to make that work either.
Unless someone can point out an obvious error here I am leaning towards this approach (http://weblog.jamisbuck.org/2007/2/5/nesting-resources) and just un-nesting these. I have a breadcrumb style navigation to show the hiearchy so that should suffice and the more I look at it the urls do get a bit unwieldily.
With nested resources, you will need to specify all the parent levels when you want to access a child level. Otherwise Rails will not know how to get to your child level. So, you will need to use first-level_second-level_comment and supply first-level and second-level values like this:
first-level_second-level_comments_path(#my_first_level, #my_second_level)
will render:
~/first-level/34/second-level/23/comments
EDIT:
I don't see why you will need to build path incrementally.
You can always build first_level comments path:
first-level_comments_path(#my_first_level)
will render
~/first-level/34/comments
Or listing of all second levels within the first level: (second-level's index action for a given first-level)
first-level_second-levels_path(#my_first_level)
will render
~/first-level/34/second-levels

Passing :new to Rails url_for

Maybe I'm stupid but Rails provides this nifty syntax for generating URL's like so:
url_for([user, comment]) # => /users/1/comment/1
Passing :edit allows me to create something like this:
url_for([:edit, user, comment]) # => /users/1/comment/1/edit
but is there some way to do following?
url_for([:new, user, comments]) # => NoMethodError: undefined method `new_user_comments_url'
UPDATE: Added more information.
My routes.rb:
resources :users do
resources :comments
end
resources :posts do
resources :comments
end
My problem here is, that I can't use Rails auto-generated url helper (user_comments_url), because I'm sharing the views for both user comments and post comments.
There are two workarounds (but no one feels like the "Rails"-way) for my problem:
Adding logic to the view, e.g. some if conditions.
Defining my own url helpers like new_parent_comment(user_or_blog).
Ok, found a solution, but I'm not sure if this is the intended one:
url_for([:new, user, :comment]) # => '/users/1/comments/new'
url_for([:new, post, :comment]) # => '/posts/1/comments/new'
Stuck with the same problem, and found next solution (tested on Rails 5.2):
url_for([user, Comment, action: :new])
where Comment model class name.
By the way, action also could be :edit.
According to the Rails Docs url_for uses the class name of the object passed to generate the RESTful route. It also states that with nested routes it can not make this assumption correctly:
If you have a nested route, such as admin_workshop_path you’ll have to call that explicitly (it’s impossible for url_for to guess that route).
I would suggest using a named route here something like new_user_comment_path(). I am assuming you have set up your routes.rb something like:
resources :users do
resources :comments do
end
end
Additionally you can run rake routes to print out the proper names for all your routes.
Hope this helps,
/Salernost
Could this simply be a typo? I think the last line should read comment, not comments:
url_for([:new, user, comment])
(Assuming your comment variable has been defined.)

Nested Routing for Single Table Inheritance model rails 3.1

I created a Single table inheritance model in my model file and am having difficulty with the routing. When I use :as in my resource, it renames my named path.
Model file:
class Account < ActiveRecord::Base
belongs_to :user
end
class AdvertiserAccount < Account
end
class PublisherAccount < Account
end
Routes.rb
resources :advertiser_accounts, :as => "accounts" do
resources :campaigns
end
I used :as in my routes because it is a single table inheritance and I want to pass the account_id and not the advertiser_account_id. My link is http://127.0.0.1:3000/advertiser_accounts/1/campaigns
/advertiser_accounts/:account_id/campaigns/:id(.:format)
However, using :as renames my named path from advertiser_account_campaigns to account_campaigns. My route looks like
account_campaigns GET /advertiser_accounts/:account_id/campaigns(.:format) campaigns#index
So when I create a new item using form_for, I would get "undefined method `advertiser_account_campaigns_path'"
Edited: current hacked solution
A hack around way that I am using is to duplicate the code in the routes file. Anyone have suggestions?
resources :advertiser_accounts, :as => "accounts" do
resources :campaigns
end
resources :advertiser_accounts do
resources :campaigns
end
If you run "rake routes" with your setup you'll see this:
account_campaigns GET /advertiser_accounts/:account_id/campaigns(.:format) campaigns#index
POST /advertiser_accounts/:account_id/campaigns(.:format) campaigns#create
new_account_campaign GET /advertiser_accounts/:account_id/campaigns/new(.:format) campaigns#new
edit_account_campaign GET /advertiser_accounts/:account_id/campaigns/:id/edit(.:format) campaigns#edit
account_campaign GET /advertiser_accounts/:account_id/campaigns/:id(.:format) campaigns#show
PUT /advertiser_accounts/:account_id/campaigns/:id(.:format) campaigns#update
DELETE /advertiser_accounts/:account_id/campaigns/:id(.:format) campaigns#destroy
accounts GET /advertiser_accounts(.:format) advertiser_accounts#index
POST /advertiser_accounts(.:format) advertiser_accounts#create
new_account GET /advertiser_accounts/new(.:format) advertiser_accounts#new
edit_account GET /advertiser_accounts/:id/edit(.:format) advertiser_accounts#edit
account GET /advertiser_accounts/:id(.:format) advertiser_accounts#show
PUT /advertiser_accounts/:id(.:format) advertiser_accounts#update
DELETE /advertiser_accounts/:id(.:format) advertiser_accounts#destroy
So you should use "account_campaingns_path" in this setup, the ":as" actually changes the calls in the code not the paths in the url. If you want to change the paths you should use ":path =>" rather than ":as =>".
The Rails guide on routing also shows some examples with ":as" and ":path" and the resulting paths and helpers, you'll need to search a bit because think they only use in in examples explaining other cases.
Edit: rereading your question, I think you may also want to look at member routes, I'm not sure if that's what you want to mean with it being a single inheritance and not wanting to pass the advertiser_account's ':account_id'?

Rails 3 - Nested resources and polymorphic paths: OK to two levels, but break at three

I'm trying to do a simple family reunion site with: "posts", "families", "kids", and "pictures". Ideally I'd like the routes/relationships to be structured this way:
resources :posts do
resources :pictures
end
resources :fams do
resources :pictures
resources :kids do
resources :pictures
end
end
In the models I have the necessary "belongs_to" and "has_many" relationships set between fams and kids. Fams, kids, and posts all are defined with "has_many :pictures, :as => :imageable" while pictures are defined as: belongs_to :imageable, :polymorphic => true
When trying to do link_to "Edit" and link_to "Destroy" in the pictures views I run into all sorts of _path problems. polymoric_path works fine at two levels, namely for posts-pictures and fams-pictures but it fails to handle the three level case of fams-kids-pictures. I'm guessing that it was not designed to handle the two levels of "imageable" objects above the picture object. Another issue is that in one instance the pictures controller has to handle a "one level" resource-nesting situation and in another it has to handle a "two levels" situation. Not sure how to approach this.
One thing I did try was to not nest resources more than one deep, per the Ruby Guides directions. I structured them like this:
resources :posts do
resources :pictures
end
resources :fams do
resources :pictures
resources :kids
end
resources :kids do
resources :pictures
end
This caused another set of problems with paths since the fam to kid relationship was no longer preserved. I also could not get polymorphic_path to function correctly accross all the different picture views.
So here is my main question: Does anyone know of a Rails 3 example/tutorial where nested resources, belongs-to/has_many, and polymorphic relationships are all put together, especially where it is not just the simple, two-level relationship that most examples show? (I'm fairly new to Rails and the Rails 2 examples I've found in these areas are confusing given my lack of Rails historical experience.)
Or can someone tell me how to structure the link_to EDIT and link_to DELETE statements for my picture views, as well as the redirect-to statement for my create, update, and destroy methods in my pictures controller?
Thanks!
Your code example that limited your nesting to 2 levels is quite near the answer. To avoid duplicate routes for fams->kids and kids, you can use the :only option with a blank array so that the 1st-level kids will not generate routes except in the context of kids->pictures, like so:
resources :posts do
resources :pictures
end
resources :fams do
resources :pictures
resources :kids
end
resources :kids, only: [] do # this will not generate kids routes
resources :pictures
end
For the above code, you can use the following to construct your polymorphic edit url:
polymorphic_url([fam, picture], action: :edit) # using Ruby 1.9 hash syntax
polymorphic_url([kid, picture], action: :edit)
Have been having this exact same problem for a while. I have it working now, but it isn't beautiful :S
From a nested monster like:
http://localhost:3000/destinations/3/accommodations/3/accommodation_facilities/52
Your params object ends up looking like this:
action: show
id: "52"
destination_id: "3"
accommodation_id: "3"
controller: accommodation_facilities
where "id" represents the current model id (last on the chain) and the other ones have model_name_id
To correctly render another nested link on this page, you need to pass in an array of objects that make up the full path, eg to link to a fictional FacilityType object you'd have to do:
<%= link_to "New", new_polymorphic_path([#destination, #accommodation, #accommodation_facility, :accommodation_facility_type]) %>
To generate this array from the params object, I use this code in application_helper.rb
def find_parent_models(current_model = nil)
parents = Array.new
params.each do |name, value|
if name =~ /(.+)_id$/
parents.push $1.classify.constantize.find(value)
end
end
parents.push current_model
parents
end
Then to automatically make the same link, you can merrily do:
<%= link_to "New", new_polymorphic_path(find_parent_models(#accommodation_facility).push(:accommodation_facility_type)) %>
Any pointers on making this solution less sketchy are very welcome :]
I can't speak for the polymorphic association problem (probably need more info on the actual error) but you are indeed headed in the right direction by defining your nested resources only one level deep. Here's a popular article by Jamis Buck that has become a reference and that you should probably check out.