Reputation:
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
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
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