Nested Rails Params Hash in a Form

Public, Rails General

harrylevine

Created: Jun 11, 2017     Updated: Jun 11, 2017


When implementing a form object approach, you'll want to create a params hash with nested areas for attributes for two different models.

Note the manager and property keys:

<ActionController::Parameters 
  {
    "utf8"=>"✓",
    "manager"=>{"manager_name"=>"Jackie ", "cell_phone"=>"303 444-1122", "company_id"=>"2", "email"=>"jackie@blah.com"},
    "property"=>{"property_name"=>"Jack's House", "address"=>"6767 Main St.", "city"=>"Denver", "state"=>"CO", "zip"=>"80112"},
    "commit"=>"Create Project management",
    "controller"=>"project_managements",
    "action"=>"create"
  }
permitted: false>

Here is the associated form:

<%= simple_form_for @project_management do |f| %>
  <%= f.input :manager_name, label: "Manager Name", input_html: { name: 'manager[manager_name]' } %>
  <%= f.input :cell_phone, label: "Manager Phone", input_html: { name: 'manager[cell_phone]' } %>
  <%= f.input :company_id, label: "Manager's Company", collection: Company.all, input_html: { name: 'manager[company_id]' } %>
  <%= f.input :email, label: "Manager Email", input_html: { name: 'manager[email]' } %>
  <%= f.input :property_name, label: "Property Name", input_html: { name: 'property[property_name]' } %>
  <%= f.input :address, label: "Property Address", input_html: { name: 'property[address]' } %>
  <%= f.input :city, label: "Property City", input_html: { name: 'property[city]' } %>
  <%= f.input :state, label: "Property State", input_html: { name: 'property[state]' } %>
  <%= f.input :zip, label: "Property Zip", input_html: { name: 'property[zip]' } %>
  <%= f.submit %>
<% end %>

Here is how you could create sanitized, white listed params for each key in the controller. Sanitized, because in this case, both the Property model and Manager model have a :name attribute that needs to be accounted for:

private

  def manager_params
    dirty_params = params.require(:manager).permit(
      :manager_name,
      :cell_phone,
      :email,
      :company_id
    )

    sanitize_params dirty_params, 'manager_name'
  end

  def property_params
    dirty_params = params.require(:property).permit(
      :manager_id,
      :address,
      :city,
      :state,
      :zip,
      :property_name
    )

    sanitize_params dirty_params, 'property_name'
  end

  def sanitize_params(dirty_params, dirty_name)
    dirty_params.tap do |params_hash|
      params_hash[:name] = params_hash.delete dirty_name
    end
  end
end

These new params methods enable you to create a new instance of each model in a transaction, like this:

private

  def create_transaction?
    ActiveRecord::Base.transaction do
      manager = Manager.create! manager_params
      manager.properties.create! property_params
    end

    true
  rescue Exception => e
    errors.add(:base, e.message)

    false
  end
end

http://surrealdetective.github.io/blog/2013/07/01/the-nested-ruby-params-hash-for-complex-html-forms-and-sinatra/