Search code examples
pythondjangodjango-rest-frameworkdjango-viewsdjango-serializer

How to write nested serializer and generic views for reverse and forward relation ships i.e foreign key relationships


Here's models.py

Class Customer(models.Model):
    ...

Class Project(models.Model):
    ...
    customer =  models.ForeignKey('Customer', on_delete=models.CASCADE)

Class Component1(models.Model):
    ...
    project = models.ForeignKey('Project', on_delete=models.CASCADE)

Class Component2(models.Model):
    ...
    project = models.ForeignKey("Project", on_delete=models.CASCADE)

Here's the serializers.py

class ProjectListSerializer(serializers.ModelSerializer):
    customer = CustomerSerializer(many=True)
    component1 = Component1Serializer()
    component2 = Component2Serializer()
    class Meta:
        model = Project

and my views.py

class ProjectList(generics.ListAPIView):
    queryset = Project.objects.all()
    serializer_class = ProjectSerializer

The view is acting as if it is a non-nested serializer. How to display the whole contents of the serializer?

Edit 1: If I use depth = 1 in meta class under serializer, the forward relation, i.e. customer will show but not the reverse relationship fields.

If I do print(repr(ProjectList())) , It is displaying exactly the format I want, but the view is unable to provide it

I am new to this platform, please comment if I can improve the question.


Solution

  • Ok, I finally understood what I was doing wrong.

    class ProjectListSerializer(serializers.ModelSerializer):
        customer = CustomerSerializer()
        component1_set = Component1Serializer(many=True) #observe the modelname_set instead 
        component2_set = Component2Serializer(many=True) # of modelname/arbitrary name, applicable for reverse relationships.
        class Meta:
            model = Project
            depth = 1 # this is for the forward relationship (i.e. Customer over here)
    
    

    If you want to set an arbitrary name, use

    ...
    somename = Component1Serializer(many=True ,source = 'modelname_set')
    ...
    

    Also keep many=True, because it is one to many relationship in a reverse manner. If you don't want to use depth for forward relations, simply use

    modelname = ModelSerializer()
    # i.e.
    customer = CustomerSerializer()
    

    There will be no change in views, but it would be easier to use ModelViewSet and a router configuration hence views.py

    
    class ProjectListViewSet(viewsets.ReadOnlyModelViewSet):
        queryset = Project.objects.all()
        serializer_class = ProjectListSerializer
    

    If your queryset needs to be dynamic for some reason, try using queryset = Project.objects.all().prefetch_related()