Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does Rails group arrays of hashes during form submission?

According to the Rails Guides and this Railscasts episode, when there's a one-to-many association between two objects (e.g. Project and Task), we can submit multiple instances of Task together with the Project during form submission similar to this:

<% form_for :project, :url => projects_path do |f| %>
  <p>
    Name: <%= f.text_field :name %>
  </p>
  <% for task in @project.tasks %>
    <% fields_for "project[task_attributes][]", task do |task_form| %>
      <p>
        Task Name: <%= task_form.text_field :name %>
    Task Duration: <%= task_form.text_field :duration %>
      </p>
    <% end %>
  <% end %>
  <p><%= submit_tag "Create Project" %></p>
<% end %>

This will result multiple copies of an HTML block like this in the form, one for each task:

<p>
    Task Name: <input name="project[task_attributes][name]">
    Task Duration: <input name="project[task_attributes][duration]">
</p>

My question is, how does Rails understand which

    (project[task_attributes][name], project[task_attributes][duration])

belong together, and packing them into a hash element of the resulting array in params? Is it guaranteed that the browsers must send the form parameters in the same order in which they appear in the source?

like image 835
K Everest Avatar asked Apr 29 '12 14:04

K Everest


1 Answers

Yes, ordering is retained as-is, as @k-everest self-answered as a comment to original question.

Those asking for HTML, see the guide on how the attribute's name is parsed.

Example of typically bad ordering:

cart[items][][id]=5
cart[items][][id]=6
cart[items][][name]=i1
cart[items][][name]=i2

And that gets parsed by Rails into this:

{ "cart"=> {"items"=> [
                        {"id"=>"5"},
                        {"id"=>"6", "name"=>"i1"},
                        {"name"=>"i2"}
                       ]}}

Example source: https://spin.atomicobject.com/2012/07/11/get-and-post-parameter-parsing-in-rails-2/

The feature was added in Rails' initial commit, with method name build_deep_hash. For more history, skip the flaming/semantics war and go for the last post from the end here: https://www.ruby-forum.com/topic/215584

like image 76
Simon B. Avatar answered Oct 30 '22 00:10

Simon B.