I'm working on an app where users can share photos. The photos can optionally belong to a collection, but don't have to.
Currently users can look through all photos via: photos/id. I think it would also make sense if they could browse through the photos for a particular collection through collections/id/photos
So, this would mean that photos were both a top level resource and a nested resource. I suppose I could set this up in the routes like so:
resources :photos
resources :collections do
resources :photos
end
Is this a good idea, or is there a better way to reuse the photo model while also allowing it to act as nested under collections when appropriate? I'd very much appreciate suggestions as to the "rails way" of handling this kind of scenario.
Thanks!
The routes you've suggested work perfectly fine. You do need to watch out in your Photos controller actions, though. Because they can be called for an individual photo OR a collection, you need to conditionally find photos based on what params are available.
Also, I'd suggest being more specific about which actions are available for each route:
resources :photos
resources :collections do
resources :photos, :only => [:index, :create, :destroy]
end
# index => show photos in a collection
# create => add a photo to a collection
# destroy => remove a photo from a collection
You don't really need to be able to edit/update/show a photo as a member of a collection from the information you provided.
Another option is to use a namespaced route:
namespace :collection, :path => '/collection', :as => :collection do
resources :photos, :only => [:index, :create, :destroy]
end
That will allow you to separate your Collection::Photos from your Photos…
controllers/photos_controller.rb
controllers/collections/photos_controller.rb
And if you really want, Rails lets you do the same to your views. Another benefit of using the namespace is that it sets up some really nifty route helpers:
photo_path(#photo) #=> /photos/1
collection_photos_path #=> /collections/1/photos
etc.
Related
My app has a Company model and User models, I've been working on the path/url structure and have managed to get it working how I wish using nested resources as shown in the code below.
I've used FriendlyID to added company_names and usernames to the models which all works fine.
The path now look how I want they to look:www.mydomain.com/company_name/username
routes.rb
resources :companies, :path => '/', only: [:show] do
# constraints has been added for usernames that include a '.'
resources :users, :path => '/', only: [:show], :constraints => { :id => /.*/ }
end
PROBLEM: I still need the ability to add a new company record but it will no longer work. I understand this is due to changing the routes but I don't understand how to remedy this as it keeps viewing
www.mydomain.com/companies/new as an unknown user with the username 'new'.
I'd be grateful if anyone can point me in the right direction or give me a hard with this.
You have set only: [:show], which it means only show method is allowed.
To create a company, need to add :new and :create.
Something like this only: [:new, :create, :show]
Note:
Always after adding or changing smth in routes file, make sure to use rake routes, Rails 5 supports rails routes also.
You can see available routes!
In my application, a User can make a Post, and a User can make Correction (think of it as a comment) on another user's post. Each User can have many Posts, and each Post can have many Corrections.
On each show page for a Post, there is a form to create a new Correction. This uses the user_post_corrections path.
On the show page for each User, I would like to display each Correction they've submitted for any Post. This requires a user_corrections path.
In order to achieve this, I have the following in my routes.rb:
resources :users do
resources :posts do
resources :corrections
end
end
resources :users do
resources :corrections
end
This intuitively feels bad to me, as I've created two nested routes that are very similar to one another.
Is there a better way to do this? My code is working fine as it is but is there a best practice method for implementing this kind of model?
Routing concerns are an excellent but underused tool for DRYing out your routes:
concern :correctable do
resources :corrections
end
# just an example of multiple concerns
concern :commentable do
resources :comments
end
resources :users, concerns: :correctable
resources :posts, concerns: [:correctable, :commentable]
However you should take when creating nested routes so that you are not nesting needlessly.
Often you might want the collective actions [new, index, create] to be scoped by the parent:
GET|POST /posts/:post_id/corrections
GET /posts/:post_id/corrections/new
While you want the member actions to be unscoped since you can always access a record directly if it has a unique id.
GET /corrections/:id
GET /corrections/:id/edit
PATCH /corrections/:id
DELETE /corrections/:id
To do this you would declare the routes like so:
resources :corrections, only: [:show, :update, :edit]
concern :correctable do
resources :corrections, only: [:new, :index, :create]
end
resources :users, :posts, concerns: [:correctable]
The shallow: true option does something like this but does not work well when you declare the same resources several times as it adds unscoped routes for every call.
I have an app with a parent resource (has_many) of patterns and a child resource (belongs_to) of snippets. My desire is to build a couple custom routes to specific pages and I am wondering the best way to do that. For now, here is what I have that is working, but I am wondering if there is a better way since the article I read on custom routing tells me this is not good practice.
I have purposefully not nested the resources because the snippets need to stand alone as well as be viewed within the context of their parent pattern.
The goal is to be able to create a few custom views like the following:
http://patterns.dev/patterns/:id/snippets //Got this one working, but incorrectly I believe
http://patterns.dev/patterns/:id/snippets/sort // Show all snippets for pattern to sort
http://patterns.dev/patterns/:id/images // Show all the images for patterns to moderate
routes.rb
Rails.application.routes.draw do
devise_for :users, :path => '', :path_names => {:sign_in => 'login', :sign_out => 'logout'}
resources :patterns
get 'patterns/:id/snippets' => 'patterns#snippets', as: 'pattern_snippets'
resources :snippets
root 'welcome#index'
end
I guess nested resources is what you need. You can specify just index action for nesting and keep all the snippets resources separately. Also you are to add some logic to snippets#index action to check params[:pattern_id]'s existance:
resources :patterns do
# this line will generate the `patterns/:id/snippets` route
# referencing to snippents#index action but within specific pattern.
# Also you have to add pattern_id column to snippets table to define the relation
resources :snippets, only: :index
end
resources :snippets
Use Collection Routes to make Rails to recognize paths like /patterns/:id/snippets/sort
resources :patterns do
resources :snippets, only: :index do
# this line will generate the `patterns/:id/snippets/sort` route
# referencing to snippets#sort action but again within specific pattern.
get :sort, on: :collection
end
end
resources :snippets
If you have Image model you can nest resources the same way like with snippets:
resources :patterns do
resources :images, only: :index
end
If it's just an action in patterns controller you can do:
resources :patterns do
get :images, on: :member
end
The goal is to create a URL like this for a GET, REST API:
/manager/someID/report
example: /manager/2/report
I can get it to show in rake routes if do it this way:
get 'manager/:id/report', to: 'report#show'
But in some weblogs I read, thats the way unskilled developers write their routes! and looks like the better way is to use "nested resources" so I am banging my head over desk to get nested resources working the same way...but no success
this is what I have written so far:
resources :manager, only: [:show] do
resources :report, only: [:show], controller: 'report' do
member do
## WAT ?!
end
end
end
First, you might want to consider reading different blogs if they're calling routes like that "unskilled".
What you proposed is actually fine considering it's a non-standard RESTful route, and maybe even preferable in some cases. If you want an alternative approach, you have a couple different options. I don't think any one is more right than the other, but I prefer the first because it takes up less vertical space.
resources :manager, only: [:show] do
get 'report' => 'report#show', on: :member
end
or
resources :manager, only: [:show] do
member do
get 'report' => 'report#show'
end
end
I have a pretty common case for nested routes, I feel like, that looks something like this (in some sort of pseudonotation):
'/:username/photos' => Show photos for User.find_by_username
'/photos' => Show photos for User.all
In a nutshell: I have users. They have photos. I want to be able to show their photos on their page. I also want to be able to show all photos, regardless of the user. I'd like to keep my routes RESTful and using the built-in resource methods feels like the right way to do it.
Option 1 for doing this is to have PhotosController#index use a conditional to check which params are given and get the list of photos and set the view (different for a user's photos than for all photos). It's even easy to route it:
resources :photos, :only => [:index]
scope ':/username' do
resources :photos
end
Boom. It'd seem like Rails was setup for this. After the routes, though, things get more complicated. That conditional back in the PhotosController#index action is just getting more and more bloated and is doing an awful lot of delgation. As the application grows and so do the number of ways I want to show photos, it is only going to get worse.
Option 2 might be to have a User::PhotosController to handle user photos, and a PhotosController to handle showing all photos.
resources :photos, :only => [:index]
namespace :user, :path => '/:username' do
resources :photos
end
That generates the following routes:
photos GET /photos(.:format) {:action=>"index", :controller=>"photos"}
user_photos GET /:username/photos(.:format) {:action=>"index", :controller=>"user/photos"}
POST /:username/photos(.:format) {:action=>"create", :controller=>"user/photos"}
new_user_photo GET /:username/photos/new(.:format) {:action=>"new", :controller=>"user/photos"}
edit_user_photo GET /:username/photos/:id/edit(.:format) {:action=>"edit", :controller=>"user/photos"}
user_photo GET /:username/photos/:id(.:format) {:action=>"show", :controller=>"user/photos"}
PUT /:username/photos/:id(.:format) {:action=>"update", :controller=>"user/photos"}
DELETE /:username/photos/:id(.:format) {:action=>"destroy", :controller=>"user/photos"}
This works pretty well, I think, but everything is under a User module and I feel like that might end up causing problems when I integrate it with other things.
Questions
Does anybody have experience with something like this?
Can anybody share a better way of handling this?
Any additional pros and cons to consider with either of these options?
Update: I've gone ahead implementing Option 2 because it feels cleaner allowing Rails' logic to work rather than overriding it. So far things are going well, but I also needed to rename my namespace to :users and add an :as => :user to keep it from clashing with my User model. I've also overridden the to_param method on the User model to return the username. Path helpers still work this way, too.
I'd still appreciate feedback on this method. Am I doing things the expected way, or am I misusing this functionality?
Have you considered using a shallow nested route in this case?
Shallow Route Nesting
At times, nested resources can produce cumbersome URLs. A solution to this
is to use shallow route nesting:
resources :products, :shallow => true do
resources :reviews
end
This will enable the recognition of the following routes:
/products/1 => product_path(1)
/products/1/reviews => product_reviews_index_path(1)
/reviews/2 => reviews_path(2)
The best way to do this depends on the application, but in my case it is certainly Option B. Using namespaced routes I'm able to use a module to keep different concerns separated out into different controllers in a very clean way. I'm also using a namespace-specific controller to add shared functionality to all controllers in a particular namespace (adding, for example, a before_filter to check for authentication and permission for all resources in the namespace).
I did something similar to this in one of my apps. You're on the right track. What I did was declare nested resources, and build the query using the flexible arel-based syntax of Active Record in Rails 3. In your case it might look something like this:
# config/routes.rb
resources :photos, :only => :index
resources :users do
resources :photos
end
# app/controllers/photos_controller.rb
def index
#photos = Photo.scoped
#photos = #photos.by_user(params[:user_id]) if params[:user_id]
# ...
end
You could define a seperate route for getting the photos for one user like so:
get '(:username)/photos', :to => 'photos#index'
But I would advise just using the nested resource that Jimmy posted above since that is the most flexible solution.
Example::Application.routes.draw do
resources :accounts, :path => '' do
resources :projects, :path => '', :except => [:index]
end
end
Got the example from:
http://jasoncodes.com/posts/rails-3-nested-resource-slugs
Just applied that in my current project.