Parent-child relationship modeling REST API

45 views Asked by At

I have a sample table below with a parent-child relationship.

id parent
1 null
2 1
3 2
4 null

I have to represent this in the UI as a tree structure. What is or are the best approaches to this? Do I fetch the data as flat from the API and restructure it as tree hierarchy at the front-end.

[
    {
        "id": 1,
        "parent": null,
    },
    {
        "id": 2,
        "parent": 1,
    },
    {
        "id": 3,
        "parent": 2,
    },
    {
        "id": 4,
        "parent": null,
    }
]

OR

The API should per-structure the response as a tree such that the UI simply consumes it.

[
    {
        "id": 1,
        "parent": null,
        "children": [
            {
                "id": 2,
                "parent": 1,
                "children": [
                    {
                        "id": 3,
                        "parent": 2,
                        "children": []
                    }
                ]
            }
        ]
    },
    {
        "id": 4,
        "parent": null,
        "children": []
    }
]

If I have to use something like the second approach, what is the most efficient way to do this such that there will not be duplicate entries.

I'm using djangorestframework and react.

1

There are 1 answers

4
chheee On

you can custom logic in serializer using serializers.SerializerMethodField()

Model

class FolderModel(models.Model):
    name = models.CharField(max_length=100)
    parent = models.ForeignKey('self', on_delete=models.CASCADE, related_name='children', null=True)

Serializer

class FolderSerializer(serializers.ModelSerializer):
    children = serializers.SerializerMethodField()

def get_children(self, obj):
    # Initialize a cache in the context if it doesn't exist
    if 'serialized_cache' not in self.context:
        self.context['serialized_cache'] = set()

    # Check if the object has already been serialized
    if obj.id in self.context['serialized_cache']:
        return []

    # Mark the object as serialized
    self.context['serialized_cache'].add(obj.id)

    # prevent deep recursion by set limit depth
    depth = self.context.get('depth', 0)
    if 10 > depth:
        self.context['depth'] = depth + 1
        return FolderSerializer(obj.children.all(), context=self.context, many=True).data
    else:
        return []

    class Meta:
        model = FolderModel
        fields = ['id', 'name', 'name', 'parent', 'children']

The implementation aims to showcase the use of context, allowing for flexibility according to your use case.

view

add filter parent=None into query set

class FolderView(viewsets.ModelViewSet):
    model = FolderModel
    queryset = FolderModel.objects.all().filter(parent=None)
    serializer_class = FolderSerializer

Anyway if you set 'queryset = FolderModel.objects.all().filter(parent=None)' in view there are no chance to hit 'Recursive Exceeded'. So you can just return serializer directly

def get_children(self, obj):
    return FolderSerializer(obj.children.all(), context=self.context, many=True).data