Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

Params hash is incorrect for nested arrays

#127

Say you have a form with fields for creating several contacts; each contact has fields for several phone numbers. The html might look like this:

<!-- first contact, 2 telephones -->
<input name="contact[][name]" ... />
<input name="contact[][telephone][][area_code]" ... />
<input name="contact[][telephone][][number]" ... />
<input name="contact[][telephone][][area_code]" ... />
<input name="contact[][telephone][][number]" ... />

The Contacts have not been created yet (they do not yet exist in the DB) so as there is no DB ID for the contacts, they will be stored in the params hash in a "contacts" array (as opposed to a "contacts" hash, which would be the case if the contacts had DB IDs). Same for the Telephones.

So this is what I'd expect in the params hash:

{ "contact" => [
    { "name" => "nnn",
      "telephone" => [
        { "area_code" => "nnn", "number" => "nnn" },
        { "area_code" => "nnn", "number" => "nnn" }
      ]
    }
  ]
}

After all, that is what the "telephone" array would look like if it wasn't nested inside "contact" -- i.e. with

<input name="telephone[][number]" />

Instead, I get something like this:

{ "contact" => [
    { "name" => "nnn" },
    { "telephone" => ["area_code" => "nnn"] },
    { "telephone" => ["number" => "nnn"] },
    { "telephone" => ["area_code" => "nnn"] },
    { "telephone" => ["number" => "nnn"] }
  ]
}

Using hashes (when the contacts & telephones have IDs), instead of arrays, works correctly.

Reported by David Röthlisberger · May 6th, 2008 @ 10:05 PM

State: invalid
Milestone: none
Assigned to: nobody
Importance: none

Activity

  1. David Röthlisberger
    David Röthlisberger

    Patch with a unit test demonstrating this nested array behaviour.

    May 6th, 2008 @ 10:01 PM

  2. Jeremy Kemper
    Jeremy Kemper
    • State changed from new to invalid

    May 7th, 2008 @ 08:35 PM

  3. Jeremy Kemper
    Jeremy Kemper

    There's nothing to distinguish which hash keys belong to which element of the array. Looks like you expect the parser to 'remember' previously-seen hash keys and start on a new array element when a key is repeated (?)

    May 7th, 2008 @ 08:37 PM

  4. David Röthlisberger
    David Röthlisberger

    Exactly. After all, it seems like it 'remembers' previous keys when there no nested arrays:

    <input name="telephone[][area_code]" ... />
    <input name="telephone[][number]" ... />
    <input name="telephone[][area_code]" ... />
    <input name="telephone[][number]" ... />
    

    correctly gives:

    "telephone" => [
      { "area_code" => "nnn", "number" => "nnn" },
      { "area_code" => "nnn", "number" => "nnn" }
    ]
    

    It seems to me that the parser is adding elements to the same hash (telephone[0]) until it sees an element with a key that is already present in the hash; then it creates a new hash (telephone[1]).

    Why should the behaviour be different simply because "telephone" is nested inside an array? (I mean "why" not as in technical reasons, but as in a user's expectations). At least in there case where I am fully specifying all parameters for each record, in the correct order (i.e. there are no "sparse" records).

    The whole parser is quite confusing and I am not the first to expect it to behave differently :-)

    See e.g. http://dev.rubyonrails.org/ticke...:11

    But I am happy with closing this; I have since learnt that the best way to achieve this is with the :index option to forms_for and fields_for, which over-rides the default behaviour of using the record's database id as the key in the params hashes.

    May 7th, 2008 @ 09:07 PM

  5. Brad Gessler
    Brad Gessler
    • Tag set to actionpack, bug

    I've had this exact same problem. Rails seems to only parse two levels deep because it doesn't remember the previously seen hash-keys.

    Don't be so quick to close the issue. It looks like they're getting there with #474 Allow Hash to be passed into AssociationProxy setters and AssociationCollections for updates and creations; but I didn't see any activity on the params parser for supporting deep model saves.

    September 7th, 2008 @ 07:33 PM