Fred Fred - 9 days ago 5
reST (reStructuredText) Question

POSTing JSON with Association data

I am trying to send a POST request to a rails scaffold controller which contains a nested array representing records that need to be created and associated with the newly created parent.

Here is some example JSON:

{
"plan_id":3,
"weight":60,
"exercise_sets": [
{
"created_at":"2012-06-13T14:55:57Z",
"ended_at":"2012-06-13T14:55:57Z",
"weight":"80.0",
"repetitions":10,
"exercise_id":1
}
]
}


..and my models..

class Session < ActiveRecord::Base
has_many :exercise_sets, :dependent => :destroy
has_many :exercises, :through => :exercise_sets
end

class ExerciseSet < ActiveRecord::Base
belongs_to :exercise
belongs_to :session
end


Is what I am attempting possible?

Ben Ben
Answer

This is certainly not impossible, though you may have to switch up your parameter naming a bit.

When you pass the JSON above to the controller, it either gets passed as parameters to a constructor:

Session.new(params[:session])

Or gets passed to the #update_attributes method on a persisted Session instance:

@session = Session.find(params[:id])
@session.update_attributes(params[:session])

Both the constructor and #update_attributes methods turn parameters like "plan_id" into assigment method calls. That is,

@session.update_attributes(:plan_id => "1")  

Turns into (inside the #update_attributes method):

@session.plan_id = "1"

So, this works for your plan_id and weight attributes, because you have both #plan_id= and #weight= setter methods. You also have an #exercise_sets= method given to you by has_many :exercise_sets. However, the #exercise_sets= method expects ExerciseSet objects, not ExerciseSet attributes.

Rails is capable of doing what you are trying to do via the #accepts_nested_attributes_for class method. Try this:

class Session < ActiveRecord::Base
  has_many :exercise_sets, :dependent => :destroy
  has_many :exercises, :through => :exercise_sets

  accepts_nested_attributes_for :exercise_sets
end

This sets up (metaprograms) an #exercise_sets_attributes= method for you. So just modify your JSON to:

{
    "plan_id":3,
    "weight":60,
    "exercise_sets_attributes": [
        {
            "created_at":"2012-06-13T14:55:57Z",
            "ended_at":"2012-06-13T14:55:57Z",
            "weight":"80.0",
            "repetitions":10,
            "exercise_id":1
        }
    ]
}

More info: http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

Comments