Warning: include() [function.include]: Unable to access /var/www/html/rogue-development/blog2/wp-content/advanced-cache.php in /var/www/html/rogue-development/blog2/wp-settings.php on line 62

Warning: include(/var/www/html/rogue-development/blog2/wp-content/advanced-cache.php) [function.include]: failed to open stream: No such file or directory in /var/www/html/rogue-development/blog2/wp-settings.php on line 62

Warning: include() [function.include]: Failed opening '/var/www/html/rogue-development/blog2/wp-content/advanced-cache.php' for inclusion (include_path='.:/usr/share/pear:/usr/share/php') in /var/www/html/rogue-development/blog2/wp-settings.php on line 62

Notice: add_option was called with an argument that is deprecated since version 2.3 with no alternative available. in /var/www/html/rogue-development/blog2/wp-includes/functions.php on line 3468

Notice: register_sidebar_widget is deprecated since version 2.8! Use wp_register_sidebar_widget() instead. in /var/www/html/rogue-development/blog2/wp-includes/functions.php on line 3382

Notice: register_widget_control is deprecated since version 2.8! Use wp_register_widget_control() instead. in /var/www/html/rogue-development/blog2/wp-includes/functions.php on line 3382
rails « Marc’s Musings

Tag Archive for 'rails'

Creating nested objects with JSON in Rails


Notice: Undefined index: STRICT_MODE_APPLIES in /var/www/html/rogue-development/blog2/wp-content/plugins/deans_code_highlighter/geshi.php on line 1036

Did you know it’s possible to send a single JSON based request to a stanard rails controller and have it create an object, plus an entire tree of children records?  I knew it was possible, but I was wracking my brain trying to figure out how to do it for several days. Imagine you have a data model something like this:

  1. class Commute < ActiveRecord::Base
  2.   has_many :locations
  3. end
  4.  
  5. class Location < ActiveRecord::Base
  6.   belongs_to :commute
  7. end

That’s a pretty simple many-to-one relationship.  Now, if you have a regular old Rails controller, how do you pass that single JSON request to create both the commute object, as well as several location children?

The first step is to add the accepts_nested_attributes_for attribute to the commute model object.

  1. class Commute < ActiveRecord::Base
  2.   has_many :locations
  3.   accepts_nested_attributes_for :locations, :allow_destroy => true
  4. end

And… that’s it on the server side!

To use this functionality, your client should send a PUT request with the following JSON in the body:

  1. {
  2.     "commute": {
  3.         "minutes": 0,
  4.         "startTime": "Wed May 06 22:14:12 EDT 2009",
  5.         "locations_attributes": [
  6.             {
  7.                 "latitude": "40.4220061",
  8.                 "longitude": "127.4220061"
  9.             },
  10.             {
  11.                 "latitude": "42.4220061",
  12.                 "longitude": "41.4220061"
  13.             }
  14.         ]
  15.     }
  16. }

minutes and starttime are two properties of the commute object. And likewise latitude and longitude are two properties of the location objects.
The only thing strange there is instead of passing a locations array, you have to pass a locations_attributes array. The “_attributes” is the magic piece that I couldn’t figure out.  This lets Rails know you want it to figure out how to create the children, and you’re not simply passing those children in.

If you don’t put the _attributes suffix in, you’ll get errors like the following because Rails is creating a generic hash and trying to shove that into where a Location object should go.

ActiveRecord::AssociationTypeMismatch (Location(#18269790) expected, got HashWithIndifferentAccess(#2654720)):

This general approach should work with XML or HTTP params based requests as well.