0

I'm new to rails and building a relational database application.

I have a somewhat complex database structure, and am trying to create a nested form that creates a child object, and then associates that child object to existing objects through a habtm relationship.

My goal is to make entering data into this database as streamlined as possible. I used Railscast #196-197 to get this far.

Partial Database Structure:

category <-- habtm --> service <-- belongs_to --> provider

Categories have many services, services may be in many categories.

Providers have many services, services belong to a single provider.

Providers may have services in many different categories.

Models:

class Provider < ActiveRecord::Base
has_many :services, :dependent => :destroy
accepts_nested_attributes_for :services, allow_destroy: true
end

class Service < ActiveRecord::Base
belongs_to :provider
has_and_belongs_to_many :categories
has_many :comments
end

I have a nested form that allows me to add a service from the same form where I am adding a provider.

I have also generated a list of categories (check boxes) to allow the user to associate the service with categories.

The problem is that when I submit the form, the join table for services_categories is not being created.

This is the information from the console that I am getting when I submit my "Create Provider" form. I can see where the category_ids[] are being stored, but they're not being passed anywhere and they aren't initiating the creation of entries in the join table.

Console Printout:

Started POST "/providers" for ::1 at 2015-05-06 14:38:36 -0700
Processing by ProvidersController#create as HTML
  Parameters: {"utf8"=>"√", "authenticity_token"=>"MJXUbLeh+pT3OSC6B2pfGnsUOBkG1
jim2GUpKD9KC72vp3trLAc4Zf+ICZ6yGLiagTGQXMsJ/j8iDpbpCBhUxQ==", "provider"=>{"name
"=>".Test Provider", "organization"=>".Test Organization", "contact"=>"", "phone
"=>"", "website"=>"", "address1"=>"", "address2"=>"", "city"=>"", "state"=>"Onli
ne", "zip"=>"", "services_attributes"=>{"0"=>{"title"=>".Test Service", "descrip
tion"=>"", "_destroy"=>"0"}}}, "category"=>{"service_ids"=>[""]}, "service"=>{"c
ategory_ids"=>["4", "37", "36"]}, "commit"=>"Create Provider"}
   (0.0ms)  begin transaction
  SQL (2.0ms)  INSERT INTO "providers" ("name", "organization", "address1", "add
ress2", "city", "state", "phone", "website", "contact", "created_at", "updated_a
t") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)  [["name", ".Test Provider"], ["org
anization", ".Test Organization"], ["address1", ""], ["address2", ""], ["city",
""], ["state", "Online"], ["phone", ""], ["website", ""], ["contact", ""], ["cre
ated_at", "2015-05-06 21:38:36.715129"], ["updated_at", "2015-05-06 21:38:36.715
129"]]
  SQL (0.0ms)  INSERT INTO "services" ("title", "description", "provider_id", "c
reated_at", "updated_at") VALUES (?, ?, ?, ?, ?)  [["title", ".Test Service"], [
"description", ""], ["provider_id", 56], ["created_at", "2015-05-06 21:38:36.731
130"], ["updated_at", "2015-05-06 21:38:36.731130"]]
   (39.0ms)  commit transaction
Redirected to http://localhost:3000/providers/56
Completed 302 Found in 84ms (ActiveRecord: 41.0ms)

My providers_controller.rb (With some irrelevant parts taken out):

      class ProvidersController < ApplicationController
### 
    def new 
        @provider = Provider.new        
        @provider.services.build
        @services = Service.all
        @service = Service.new
    end 

    def edit 
        @provider = Provider.find(params[:id])
    end

    def create 
        @provider = Provider.new(provider_params)

        if @provider.save 
            redirect_to @provider
        else 
            render 'new' 
        end 
    end

    ###

    def get_all_categories 
        @categories = Category.find(:all, :order => 'name')
    end 

    private 
        def provider_params 
            params.require(:provider).permit(:name, :organization, :address1, :address2, :city, :state, :zip, :phone, :website, :contact, :service_ids => [], services_attributes: [:id, :title, :description, :_destroy, :category_ids => []])
        end 

end

As you can see, the attribute :category_ids =>[] isn't showing up in the list of service attributes in the console.

The relevant parts of the form that I am using to submit provider data:

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


<%= f.fields_for :services do |builder| %> 

    <%= render "services_fields", :f => builder %>

<% end %>

<%= f.submit %> 

The form partial for Add New Service:

    <%= f.label :title, "Service Title" %><br/> 
<%= f.text_field :title %> <br/>            

<%= f.label :description, "Service Description" %><br/> 
<%= f.text_area :description, :rows => 3 %> <br/>

<p> 
    <label>Associated Categories</label>

<p> 
    <label>Associated Categories</label>
    <div>
        <%= hidden_field_tag "category[service_ids][]", nil %>
        <% Category.all.sort_by{|category| category.name}.each do |category| %>     
            <%= check_box_tag "service[category_ids][]", category.id, 
            @service.categories.include?(category), id: dom_id(category) %>
            <%= label_tag dom_id(category), category.name %></br>
    </div>
        <% end %> 
</p>


<%= f.check_box :_destroy %> 
<%= f.label :_destroy, "Remove Service" %><br/>

I'm interested in any way that you can think of to be able to

  • Add Provider
  • Add Service
  • Associate Service and Category

all in one form.

I have thought about having a pop-up window to take the user to the original Add Service form, which associates Categories, but I'm new to Rails, and I don't know how difficult that would be. I also feel like I ought to be able to do it all on one page.

I am pretty set on having a habtm relationship between Service and Categories. It works well for all other parts of the app.

Please let me know if I can post any other relevant code snippets. Thanks!

1 Answers1

0

The attribute :category_ids =>[] isn't showing up in the list of service attributes in the console because it isn't included in params["provider"]["service_attributes"], it's inside params["service"].

This means that the category_ids aren't being accessed by the create action.

If you want associate the created service and the categories without modifying your form, you can try referencing newly created service and then updating its category_ids with 'params["service"]', or just add in the category_id's into params["provider"]["service_attributes"] before creating the provider:

params["provider"]["service_attributes"]["category_ids"] = params["service"]["category_ids"]

Although this would require you to re-whitelist the permitted attributes from params.

Or you can modify your form so that the category_ids end up nested within service_attributes in the params hash.

  • I really appreciate the time you took to answer. I am reading and re-reading your answer, and have tried a few ideas of how to implement it, without any success. What would I add to the controller to implement, for example: **referencing newly created service and then updating its category_ids with 'params["service"]** – Tracy Stinghen May 07 '15 at 17:26