Jeremy Thomas
Jeremy Thomas

Reputation: 6684

Rails 4: Complex forms and nested attributes

I have a Rails 4 app where I have two related controllers, potential_client and location. I am trying to enable to creation of potential clients through the location controller. I believe there is something wrong with my form because it gets submitted as a PATCH to the location, rather than creating a new potential_client as follows:

class Location < ActiveRecord::Base
    validates_presence_of :name, :email, :address, :phone
    has_many :potential_clients
    accepts_nested_attributes_for :potential_clients
end

and

class PotentialClient < ActiveRecord::Base
    validates_presence_of :name, :email, :phone
    belongs_to :location
end

I have my routes configured such that:

resources :locations do
    resources :potential_clients
end

and in my app/views/locations/show.html.erb I have the following form:

<div class="form-container">
    <%= form_for @location do |f| %>
        <%= f.fields_for :potential_client do |pc_form| %>

            <%= pc_form.label :name %>
            <%= pc_form.text_field :name %><br />

            <%= pc_form.label :email %>
            <%= pc_form.email_field :email %><br />

            <%= pc_form.label :phone %>
            <%= pc_form.number_field :phone %><br />

            <%= pc_form.label :message %>
            <%= pc_form.text_field :message %><br />


            <%= pc_form.hidden_field :location_id, :value => @location.id %>

            <%= pc_form.submit "Submit" %>

        <% end %>
    <% end %>
</div>

The form loads properly, but when I try to submit something, I get an error in my console that Unpermitted parameter: potential_client, but in my location_controller I have:

def location_params
    params.require(:location).permit(:name, :email, :address, :phone, :potential_client, potential_client_attributes: [:name, :email, :message, :phone])
end

...and to top it off, when I am trying to CREATE a potential_client, my console says:

Started PATCH "/locations/1" for ::1 at 2016-02-03 13:18:27 -0500
ActiveRecord::SchemaMigration Load (0.1ms)  SELECT "schema_migrations".* FROM "schema_migrations"
Processing by LocationsController#update as HTML

all routes if they are helpful:

location_potential_clients     GET    /locations/:location_id/potential_clients(.:format)          potential_clients#index
                               POST   /locations/:location_id/potential_clients(.:format)          potential_clients#create
 new_location_potential_client GET    /locations/:location_id/potential_clients/new(.:format)      potential_clients#new
edit_location_potential_client GET    /locations/:location_id/potential_clients/:id/edit(.:format) potential_clients#edit
     location_potential_client GET    /locations/:location_id/potential_clients/:id(.:format)      potential_clients#show
                               PATCH  /locations/:location_id/potential_clients/:id(.:format)      potential_clients#update
                               PUT    /locations/:location_id/potential_clients/:id(.:format)      potential_clients#update
                               DELETE /locations/:location_id/potential_clients/:id(.:format)      potential_clients#destroy
                     locations GET    /locations(.:format)                                         locations#index
                               POST   /locations(.:format)                                         locations#create
                  new_location GET    /locations/new(.:format)                                     locations#new
                 edit_location GET    /locations/:id/edit(.:format)                                locations#edit
                      location GET    /locations/:id(.:format)                                     locations#show
                               PATCH  /locations/:id(.:format)                                     locations#update
                               PUT    /locations/:id(.:format)                                     locations#update
                               DELETE /locations/:id(.:format)                                     locations#destroy

perhaps its because :location_id and :id are the same for both routes and Rails is routing to location instead of potential_client

Upvotes: 0

Views: 1447

Answers (1)

max
max

Reputation: 102036

Why Patch?

When you do:

<%= form_for @location do |f| %>

The rails polymorphic route helpers look at @location and uses @location.new_record? to see if it should route to update or create.

So in your locations#show action you are passing a persisted record - so it will route to update.

If you want to have a separate form which posts only a potential client you would do it like this:

<%= form_for [@location, @potential_client] do |f| %>

Which would create a POST locations/1/potential_clients request if its a new record and PATCH locations/1/potential_clients/1 if it has been persisted.

Nested attributes

Its a simple pluralization error. Your model accepts_nested_attributes_for :potential_clients while your form has <%= f.fields_for :potential_client do |pc_form| %>.

When you use fields_for is should use the same name as the relation - so in the case of has_many it should be the plural form.

<div class="form-container">
    <%= form_for @location do |f| %>
        <%= f.fields_for :potential_clients do |pc_form| %>
            # ...
        <% end %>
    <% end %>
</div>

Note that the proper param key is potential_clients_attributes so that what you should be whitelisting.

def location_params
    params.require(:location)
          .permit(
            :name, :email, :address, :phone, 
            potential_clients_attributes: [:name, :email, :message, :phone]
          )
end

Upvotes: 3

Related Questions