Friday, May 6, 2011

Rails nested resources

I'm trying to get my head around nested associations in Rails using ActiveResource. My example is as follows: What I have is an airport with many runways.

My show action in airports controller contains: @airport = Airport.find(params[:id])

When I call http://localhost/airports/2.xml I get that piece of XML:

<airport>
  <code>DUS</code>
  <created-at type="datetime">2009-02-12T09:39:22Z</created-at>
  <id type="integer">2</id>
  <name>Duesseldorf</name>
  <updated-at type="datetime">2009-02-12T09:39:22Z</updated-at>
</airport>

Now, I changed the action to

@airport = Airport.find(params[:id], :include => :runways)

How can I achieve that above loading above URL is giving me something like:

<airport>
  <code>FRA</code>
  <created-at type="datetime">2009-02-12T09:39:22Z</created-at>
  <id type="integer">2</id>
  <name>Frankfurt</name>
  <updated-at type="datetime">2009-02-12T09:39:22Z</updated-at>

  <runways>
    <runway>
      <id>1</id>
      <name>bumpy runway</name>
    </runway>
  </runways>

</airport>

And on top of that: If I have a client with

class Airport < ActiveResource::Base
  ..
end

and

class Runway < ActiveResource::Base
  ..
end

How can I get it to automatically load associations like:

a = Airport.find(1)
puts a.runways.length
=> 1

And (last but not least): Is there a way to store data from the client like:

a = Airport.find(1)
a.runways << Runway.find(1)
a.save

Maybe I'm really too blind, but I'm stuck... Any idea is warmly welcome.

Thanks

Matt

From stackoverflow
  • Resolved it myself finally. Wasn't aware to put the include into the render statememt:

    def show
      @airport = Airport.find(params[:id], :include => :runways)
      respond_to do |format|
        format.html # show.html.erb
        format.xml  { render :xml => @airport.to_xml(:include => :runways) }
      end
    end
    
    Stefan : Thank you. This isn't documented in many places. I've had to search for a long time.
  • The :include option for the finder specifies that it should eagerly fetch the related items from the database. The :include option for to_xml specifies that it should be included in the XML rendering.

    If the canonical XML representation includes the related objects, you can override the to_xml method to make your life a little simpler:

    class Airport
      def to_xml(options={})
        super(options.merge(:include => :runways))
      end
    end
    

    and then since render will call to_xml if you don't, your controller code can simply be

    format.xml { render :xml => @airport }
    
    Matt : thanks, makes perfect sense!

0 comments:

Post a Comment