rails-api / active_model_serializers

ActiveModel::Serializer implementation and Rails hooks
MIT License
5.33k stars 1.39k forks source link

Integration with awesome_nested_set #2045

Open RescuePenguin opened 7 years ago

RescuePenguin commented 7 years ago

So I'm running into an issue currently with serializing nested sets, using the Awesome_nested_set gem. I want to get a Category, and serialize it, all of its children, and their children, down to the bottom, and all of its parents, and their parents, all the way to the top.

Currently I can include some children/parents but can only ascend a designated distance. I can use include: 'children' or include: 'children.children' but it only goes so far. If I use include: 'children.**' to try and include everything I hit an infinite loop because that children will call its parent, and that parent will call its children, etc.

class Category < ApplicationRecord
  acts_as_nested_set

  has_many :products
  has_one :image, as: :parent
end
class CategorySerializer < ActiveModel::Serializer
  attributes :id, :name, :image

  belongs_to :parent
  has_many :children
end
def index
   @categories = Category.roots
   render json: @categories, include: 'children.children, parent.parent, image'
end

Awesome_nested_set has a method that I can use to get all of the categories in a tree, but instead of nesting them, it places them all into a single array. I need them nested into the JSON so that it can easily be used by our front-end applications.

ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux] Rails 5.0.0.1 active_model_serializers (0.10.2)

bf4 commented 7 years ago

I hit an infinite loop

yeah, that's why deep nesting isn't recommended :)

RescuePenguin commented 7 years ago

Yeah, I've seen that mentioned, was hoping that I was just missing something. So are my best chances to just include a a limited number of children, and hope that it's enough? It can be somwhat regulated and i expect the result to be around 5 or 6 levels deep, but still seems like a rather weird/poor work-around.

ChrisHampel commented 7 years ago

in the case of Awesome Nested Set or other tree structures you can also instead just flatten the tree into an array in either of these forms to let the code on the other end reconstruct it into a tree

  1. parent_id, lft,rgt, ...
  2. parent_id, position(as the position under the parent, which would be parent.lft-self.lft), ...
NateJBeck commented 7 years ago

I've had some success serializing JSON to reflect a children > children > children ... structure using the following. In my example a Job has_many :steps, but you may be able to use the same attributes :children override for your Categories.

class JobSerializer < ActiveModel::Serializer
    class StepSerializer < ActiveModel::Serializer
        attributes :description, :position

        # children attribute provided by awesome_nested_set gem
        attributes :children

        def children
            object.children.map { |child| StepSerializer.new(child) }
        end
    end

    has_many :steps, serializer: StepSerializer
end

There's probably a cleverer & faster way to do this than with .map, but I've yet to hit an infinite loop and end up with JSON that works well with my front-end.

screen shot 2017-06-23 at 10 56 56