← Back to all snippets

form_for with nested shallow resources

David Anderson | May 21, 2019

Given the following nested shallow routes for venues, the venue’s collection actions will be scoped under the parent but its member actions will not be nested.

# config/routes.rb

resources :organizations do
  resources :venues, shallow: true
$ rake routes
  Prefix              Verb   URI Pattern                                                                              Controller#Action
  organization_venues GET    /organizations/:organization_id/venues(.:format)                                         venues#index
                      POST   /organizations/:organization_id/venues(.:format)                                         venues#create
new_organization_venue GET    /organizations/:organization_id/venues/new(.:format)                                     venues#new
           edit_venue GET    /venues/:id/edit(.:format)                                                               venues#edit
                venue GET    /venues/:id(.:format)                                                                    venues#show
                      PATCH  /venues/:id(.:format)                                                                    venues#update
                      PUT    /venues/:id(.:format)                                                                    venues#update
                      DELETE /venues/:id(.:format)                                                                    venues#destroy
        organizations GET    /organizations(.:format)                                                                 organizations#index
                      POST   /organizations(.:format)                                                                 organizations#create
     new_organization GET    /organizations/new(.:format)                                                             organizations#new
    edit_organization GET    /organizations/:id/edit(.:format)                                                        organizations#edit
         organization GET    /organizations/:id(.:format)                                                             organizations#show
                      PATCH  /organizations/:id(.:format)                                                             organizations#update
                      PUT    /organizations/:id(.:format)                                                             organizations#update
                      DELETE /organizations/:id(.:format)                                                             organizations#destroy

Typically for a venue objects form, you’ll define it as form_for(@venue) but that will generate the form url as /venues for a new object or /venues/:id for an existing object. How do you get the form for the new object to post to the nested route (/organizations/:organization_id/venues) and the form for the existing object to post to the /venues/:id route?

Simply define your form_for with the organization object defined for your new form and set to nil for your edit form.

# app/controllers/venues_controller.rb

def new
  @organization = Organization.find(params[:organization_id])
  @venue = Venue.new

def edit
  @venue = Venue.find(params[:id])
# app/view/venues/_form.html.haml

= form_for [@organization, @venue] do |f|
  //your form here

In the edit action we haven’t defined the @organization variable and because that’s nil, Rails will post to the second object only.

