← 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
end
$ 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
end

def edit
  @venue = Venue.find(params[:id])
end
# 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.

SUBSCRIBE

Drop your email in the box below to subscribe to my newsletter. Once per week you'll get Ruby/Rails tips, guides, job postings, and general thoughts from the web developer trenches.