Tag: rails

  • Using rails and respond_to to include nested data

    I want to display data from a nested table in my XML output using respond_to. I searched google a bit and it seems :include was the way to go. However I had some problems getting this to work properly.

    I have two models (customer and user) that are linked together. When I fetch the data for a single user, I want my xml output to include the customer data.

    The Customer model:

    class Customer < ActiveRecord::Base
      has_many :users
    end
    

    The User model:

    class User < ActiveRecord::Base
      belongs_to :customer
    end
    

    In my UserController I use the respond_to method to respond to html and xml data.
    The show action is as follows:

    # Shows the seleted user
    def show
      @user = User.find(params[:id])
      
      respond_to do |format|
        format.html
        format.xml { render :xml => @user) }
      end
    end
    

    Calling the show action on the user controller as xml should yield something like:

    <user>
      ...
      <customer>
        ...
      </customer>
    </user>
    

    But the customer data is not included.
    This puzzled me a bit.

    Searching a bit on google got me the following answer:

    # Shows the seleted user
    def show
      @user = User.find(params[:id])
      
      respond_to do |format|
        format.html
        format.xml { render :xml => @user.to_xml(:include => @user.customer) }
      end
    end
    

    source: http://rubydoc.info/docs/rails/3.0.0/ActionController/MimeResponds

    (I also tried fetching the customer out separately, that didn’t work either)

    This gave me the following error:

    undefined method `macro' for nil:NilClass
    

    The solution

    After a lot more searching I found out, that you shouldn’t pass an object, but a symbol. The name of the symbol is the data block you want to show, so in my case it was :customer

    The code to fix it was:

    # Shows the seleted user
    def show
      @user = User.find(params[:id])
      
      respond_to do |format|
        format.html
        format.xml { render :xml => @user.to_xml(:include => :customer) }
      end
    end
    

    The data for the customer now returned as well.

    Need more data?

    If you need data from several tables, just use an array of symbols instead.

    # Shows the seleted user
    def show
      @user = User.find(params[:id])
      
      respond_to do |format|
        format.html
        format.xml { render :xml => @user.to_xml(:include => [:customer, :table2, :table3]) }
      end
    end
    

    Hope you can use this tip.