简体   繁体   中英

How can I get strong parameters to permit nested fields_for attributes?

I have a form with a nested fields_for. I have attempted to permit the parameters that are generated by that nested form, but they are being blocked by strong parameters. I'm using rails 4.2.4 and ruby 2.2.2

I've read some official documentation:

I've read what looks like relevant SO posts:

I've read various blog posts:

I think I'm following what they say to do, but my nested attributes get rejected by strong parameters. I get things like Unpermitted parameters: __template_row__, 0, 1, 2, 3 in my log.

Here's my code:

models/enclosure.rb

class Enclosure < ActiveRecord::Base
  has_many :resident_animals, -> { order("year DESC, month DESC") }, dependent: :restrict_with_error
  validates :name, presence: true, uniqueness: {case_sensitive: false}

  accepts_nested_attributes_for :resident_animals, allow_destroy: true, reject_if: :all_blank

  def to_s
    name
   end
end

models/resident_animal.rb

class ResidentAnimal < ActiveRecord::Base
  belongs_to :enclosure

  validates_presence_of :enclosure, :year, :month, :color
  ...
end

controllers/enclosures_controller.rb

class EnclosuresController < ApplicationController
  ...
  def update
    @enclosure = Enclosure.find(params[:id])
    @enclosure.update(enclosure_params)
    respond_with @enclosure
  end

  private

  def enclosure_params
    params.require(:enclosure).permit(:name, :description, resident_animals_attributes: [:year, :month, :color, :id, :_destroy])
  end
end

views/enclosures/_form.html.erb

<p class="field">
  <%= form.label :name %>
  <%= form.text_field :name %>
</p>

<p class="field">
  <%= form.label :description %>
  <%= form.text_area :description %>
</p>

<fieldset>
  <legend>Resident Animals</legend>

  <table id="resident-animal-rows">
    <thead>
      <th>Year <span class="required-field">*</span></th>
      <th>Month <span class="required-field">*</span></th>
      <th>Color <span class="required-field">*</span></th>
      <th>Remove</th>
    </thead>
    <tbody>
      <%= form.fields_for :resident_animals_attributes, ResidentAnimal.new(channel: form.object, year: Date.current.year, month: Date.current.month), index: "__template_row__" do |resident_animal_fields| %>
      <tr class="resident-animal-row row-template">
        <td><%= resident_animal_fields.number_field :year %></td>
        <td><%= resident_animal_fields.select :month, month_options, include_blank: true %></td>
        <td><%= resident_animal_fields.text_field :color %></td>
        <td class="checkbox-cell"><%= resident_animal_fields.check_box :_destroy %></td>
      </tr>
      <% end %>
      <%= form.fields_for :resident_animals do |resident_animal_fields| %>
        <tr class="resident-animal-row">
          <td><%= resident_animal_fields.number_field :year %></td>
          <td><%= resident_animal_fields.select :month, month_options, include_blank: true %></td>
          <td><%= resident_animal_fields.text_field :color %></td>
          <td class="checkbox-cell">
            <%= resident_animal_fields.hidden_field :id %>
            <%= resident_animal_fields.check_box :_destroy %>
          </td>
        </tr>
      <% end %>
    </tbody>
  </table>
  <%= link_to "Add resident animal", "#", class: "resident-animal-row-add" %>
</fieldset>

When I log my parameters they look like:

{"enclosure"=>{"name"=>"Polar Quest", "description"=>"Polar bear attraction", "resident_animals_attributes"=>{"__template_row__"=>{"year"=>"2015", "month"=>"9", "color"=>"", "_destroy"=>"0"}, "0"=>{"year"=>"2005", "month"=>"8", "color"=>"white", "id"=>"1", "_destroy"=>"0"}, "1"=>{"year"=>"2012", "month"=>"7", "color"=>"yellow", "id"=>"2", "_destroy"=>"0"}, "2"=>{"year"=>"2011", "month"=>"3", "color"=>"white", "id"=>"4", "_destroy"=>"0"}, "3"=>{"year"=>"2006", "month"=>"2", "color"=>"yellowish", "id"=>"3", "_destroy"=>"0"}}}, "commit"=>"Update", "id"=>"1"}

Calling enclosure_params returns:

{"name"=>"Polar Quest", "description"=>"Polar bear attraction", "resident_animals_attributes"=>{}}

What am I doing wrong?

Thank you!

I'm going to add that correct answer from your comment here, just so this question can have a correct answer:

The problem is that .permit -ing a nested hash that include IDs as the keys and a hash of other attributes as the values is a special case (as is apparent, since it doesn't match the typical argument structure for .permit ).

The trick is: the algorithm detects the special case (called fields_for_style? because it is the style of params typically submitted by the fields_for helper) if, and only if, all of the keys translate to integers! Therefore, if you have a non-integer value (such as __template_row__ or new_record_id ) in the set of keys, it will not detect a special case, and instead reject every key in the hash that is not explicitly permitted (as for any typical hash).

In order to get around this, given the structure of params from the original post, you can simply remove the non-integer key and attributes submitted as part of the template row:

def enclosure_params
  params[:enclosure][:resident_animals_attributes].delete(:__template_row__)
  params.require(:enclosure).permit(...) # (no change from OP)
end

(Of course this means, you should be sure your interface does not try to submit meaningful data as part of your template row) ;)

def enclosure_params
  params.require(:).permit(:name, :description,
    resident_animals_attributes: [:enclosure_id, :year, :month, :color]
  )
end

May I suggest you use the newer format of rails validations:

validates: :enclosure_id, presence: true    
validates: :year, presence: true
validates: :month, presence: true
validates: :color, presence: true

You may need to use inverse: on the dependent model if you really need to require presence of enclosure on resident_animal. I'm not sure you need that validation.

The error is referring to this line

<%= form.fields_for :resident_animals_attributes, ResidentAnimal.new(channel: form.object, year: Date.current.year, month: Date.current.month), index: "__template_row__" do |resident_animal_fields| %>

in particular

index: "__template_row__" do |resident_animal_fields| 

You do not have an index attribute defined. Try removing that key value pair.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM