user393964
user393964

Reputation:

Rails to_json with all relations

I have an Item class that also contains many items (using has_many). What I want to do is read all items that have no parent (the top level) and all their subitems. So basically I need my entire Item table nested correctly, into json.

This is the code I'm using right now, but this only returns the top level items and their items, it doesn't go any lower than that. (So I'm left with only two levels)

@items = Item.where("item_id IS ?" , nil).order("position")
respond_to do |format|
  format.json { render :json => @items.to_json(:include => :items)}
end

Upvotes: 1

Views: 914

Answers (2)

Billy Chan
Billy Chan

Reputation: 24815

I do not suggest override as_json or to_json.

The reason is you'll need the raw data elsewhere, and you may need data in other format. Manipulate as_json will finally change the data, and you have no way to extend.

Using a decorator is the way to go. A good choice the gem ActiveModel Serializers. Basically it work like this

class ItemSerializer < ActiveModel::Serializer
  attributes :id, :title, :body
  has_many :comments # The JSON output will have comments inside.
end

And then in your controller:

@items = Item.where("item_id IS ?" , nil).order("position")
respond_to do |format|
  format.json { render :json => @items }
end

Your @items will be serialized by ItemSerializer automatically.

Or you can choose a custom serializer

render json: @items, each_serializer: ItemWithAssociatedSerializer

Upvotes: 1

m_x
m_x

Reputation: 12564

First, I would recommend using a gem like ancestry or awesome nested set. It will help you manage your Items tree structure efficiently (retrieving a whole hierarchy in one SQL query, and so on).

then you can do something like this :

class Item
  def as_json(options={})
    super({include: items}.merge options)
  end
end

this means that calling to_json on an item would by default include the children's json ; so recursively we go down the whole hierarchy.

If you need a way to limit the number of nested levels, you can do :

class Item
  def as_json(options={})
    return super({include: items}.merge options) unless options[:depth].present?
    if options[:depth] > 0 
      super({include: items, depth: options[:depth] - 1}.merge options)
    else
      super
    end
  end
end

Upvotes: 0

Related Questions