Rails 4 nested attribute hash keys showing as unpermitted

隐身守侯 提交于 2019-12-11 13:42:34

问题


I have a Category model and a Standard model. A category has many standards through a "join table" model CategoryStandard. In my view, I have a form where I can edit the category description, and then add or remove standards from that category. So, my nested attributes are for :category_standards, because I'm not editing the standard itself, just adding or removing relationships, if that makes sense.

Here's the important part of the view:

<%= form_for(@category) do |f| %>
...
  <div class="field">
    <%= f.label :description %>
    <%= f.text_field :description %>
  </div>

  <%= label_tag nil, "Standards in this Category" %>
  <div id="standard-list">
    <%= f.fields_for :category_standards do |ff| %>
      <div class="field">
        <%= ff.object.standard.number_with_exceptions %>
        <%= ff.hidden_field :standard_id %>
        <%= ff.hidden_field :_destroy %>
        <%= link_to "<span class='glyphicon glyphicon-remove'></span>".html_safe, "", class: "del-std-btn", title: "Remove standard from category" %>
      </div>
    <% end %>

    <div class="hidden" id="std-add-new-template">
      <div class="field">
        <%= f.fields_for :category_standards, CategoryStandard.new, child_index: "new_id" do |ff| %>
          <%= ff.collection_select :standard_id, @standards - @category.standards, :id, :number_with_exceptions, prompt: "Select a standard to add" %>
        <% end %>
      </div>
    </div>
  </div>
...
<% end %>

There's some jQuery under the hood to manipulate the "rows", but that works fine and I don't think it's part of my problem, so I'll omit it.

In my Category model, I have:

class Category < ActiveRecord::Base
  has_many :category_standards, dependent: :destroy
  has_many :standards, through: :category_standards
  validates :description, presence: true,
                          uniqueness: true
  accepts_nested_attributes_for :category_standards, allow_destroy: true, reject_if: proc { |attributes| attributes['standard_id'].blank?}
end

And in my Categories controller, I have:

def category_params
  params.require(:category).permit(:description, category_standards_attributes: [:id, :standard_id, :_destroy])
end

But when I try to add a standard to a category, I get these lines in my server log (reformatted in the hopes of making it more readable):

Parameters: {"utf8"=>"✓",
              "authenticity_token"=>"***********",
              "category"=>{
                "description"=>"Drinking Water System Components", 
                "category_standards_attributes"=>{
                  "0"=>{
                    "standard_id"=>"2",
                    "_destroy"=>"false",
                    "id"=>"1"
                  }, 
                  "new_id"=>{
                    "standard_id"=>""
                  },
                  "1424899001814"=>{
                    "standard_id"=>"1"
                  }
                }
              },
              "commit"=>"Save Changes",
              "id"=>"2"
            }

User Load (5.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1

Category Load (4.0ms)  SELECT "categories".* FROM "categories" WHERE "categories"."id" = $1 LIMIT 1  [["id", "2"]]

Unpermitted parameters: 0, new_id, 1424899001814

(4.0ms)  BEGIN
Category Exists (6.0ms)  SELECT 1 AS one FROM "categories" WHERE ("categories"."description" = 'Drinking Water System Components' AND "categories"."id" != 2) LIMIT 1

SQL (6.0ms)  UPDATE "categories" SET "description" = $1, "updated_at" = $2 WHERE "categories"."id" = 2  [["description", "Drinking Water System Components"], ["updated_at", Wed, 25 Feb 2015 21:16:44 UTC +00:00]]

It updates the description field just fine, but what's up with the Unpermitted parameters? My attributes hash comes out just like the example in the Rails Guide on nested forms, and it even says "The keys of the :addresses_attributes hash are unimportant, they need merely be different for each address." And yet it's the keys that are getting denied for me.

Where have I gone wrong? Thanks!


回答1:


Figured it out, after a lot of reading. The missing piece was here.

Hashes with integer keys are treated differently and you can declare the attributes as if they were direct children. You get these kinds of parameters when you use accepts_nested_attributes_for in combination with a has_many association:

# To whitelist the following data:
# {"book" => {"title" => "Some Book",
#             "chapters_attributes" => { "1" => {"title" => "First Chapter"},
#                                        "2" => {"title" => "Second Chapter"}}}}

params.require(:book).permit(:title, chapters_attributes: [:title])

The important part of that was "Hashes with integer keys". My hash keys were passing as "0", "new_id", "1240934304343". It isn't important that I use "new_id", because that's just a placeholder value that gets changed in my jQuery when new rows are added. Only the template row retains that value, which is fine, because it gets filtered out by my reject_if clause.

But the fact that "new_id" isn't an integer apparently was the thing that was mucking it all up. So I changed it to "-1", which Rails accepts (even though it is still filtered out by reject_if, as it should be).

<div class="hidden" id="std-add-new-template">
  <div class="field">
    <%= f.fields_for :category_standards, CategoryStandard.new, child_index: "-1" do |ff| %>
      <%= ff.collection_select :standard_id, @standards - @category.standards, :id, :number_with_exceptions, prompt: "Select a standard to add" %>
    <% end %>
  </div>
</div>



回答2:


Your attribute keys don't seem to match what you are expecting in your strong parameters, "new_id" and "1424899001814" certainly will not be permitted.

"new_id"=>{
    "standard_id"=>""
},
"1424899001814"=>{
    "standard_id"=>"1"
}

I suspect the way you are constructing your form is invalid. Try breaking it down to the simplest working form.. like:

    <div id="standard-list">
        <%= link_to "<span class='glyphicon glyphicon-remove'></span>".html_safe, "", class: "del-std-btn", title: "Remove standard from category" %>

        <div class="hidden" id="std-add-new-template">
          <div class="field">
            <%= f.fields_for :category_standards do |ff| %>
                <%= ff.object.standard.number_with_exceptions %>
                <%= ff.hidden_field :standard_id %>
                <%= ff.hidden_field :_destroy %>
                <%= ff.collection_select :standard_id, @standards - @category.standards, :id, :number_with_exceptions, prompt: "Select a standard to add" %>
            <% end %>
          </div>
        </div>
    </div>

The intention is to have only one nested form, and by stripping it down create only one nested hash.

"category_standards_attributes"=>{
    "0"=>{
        "standard_id"=>"2",
        "_destroy"=>"false",
        "id"=>"1"
    }
}

What happens with this?



来源:https://stackoverflow.com/questions/28730715/rails-4-nested-attribute-hash-keys-showing-as-unpermitted

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!