Exclude fields when nesting serializer Django REST Framework

Viewed 2358

serializers.py

class ThreadSerializer(serializers.ModelSerializer):

    author = UserSerializer(read_only=True)
    last_post_user = UserSerializer(read_only=True)

    class Meta:
        model = Thread
        fields = '__all__'
        read_only_fields = ('locked', 'views', 'post_count', 'last_post_time')


class ChannelSerializer(serializers.ModelSerializer):

    threads = ThreadSerializer(many=True, read_only=True)

    class Meta:
        model = Channel
        fields = ['id', 'name', 'description', 'category', 'threads']


class CategorySerializer(serializers.ModelSerializer):

    channels = ChannelSerializer(many=True, read_only=True)

    class Meta:
        model = Category
        fields = ['id', 'name', 'channels']

You'll notice I have nested ChannelSerializer in CategorySerializer, when I visit the endpoint that queries data using CategorySerializer I was hoping it would list out only data from ChannelSerializer however it lists data from ThreadSerializer as well, I'd assume this is expect behavior since ThreadSerializer is nested inside ChannelSerializer.

Is there some elegant way I can exclude fields when I nest serializers? in this case I'd like to exclude threads from appearing when querying data using the CategorySerializer

image

I tried the answer from a similar question but can't seem to get it working in my case unfortunately, it ends up listing the threads again, also I'd think there's a simpler way to do it.

2 Answers

Creating a different serializer and removing the fields you do not want there (see this answer) is a better option, but here's an alternative:

class CategorySerializer(serializers.ModelSerializer):

    channels = ChannelSerializer(many=True, read_only=True)

    # Override so we can remove unwatned fields
    def to_representation(self, instance):
        rep = super().to_representation(instance)
        del rep["channels"]["threads"]
        return rep

    ...

In this case, the "threads" key won't be in the API's response, but the queries that return threads for each channel will still run (in other words, it works, but is less performant).

I've got a simple snippet for that using Different serializer. You could extend that in case.

Serializers.py

class CSerializer(serializers.ModelSerializer):

  class Meta:
    model = C
    fields = ('id', 'f1')

class BSerializer(serializers.ModelSerializer):
  c = CSerializer(many=True, read_only=True)

  class Meta:
    model = B
    fields = ('id', 'f1', 'c')

class ASerializer(serializers.ModelSerializer):
  b = BSerializer(many=True, read_only=True)

  class Meta:
    model = A
    fields = ('id', 'f1', 'b')


# --------------------------------------------------- #
class CustomBSerializer(serializers.ModelSerializer):

  class Meta:
    model = B
    fields = ('id', 'f1')

class CustomASerializer(serializers.ModelSerializer):
  b = CustomBSerializer(many=True, read_only=True)

  class Meta:
    model = A
    fields = '__all__'

# --------------------------------------------------- #

Views.py

class AView(generics.ListAPIView):
  """List C objects"""
  queryset = A.objects.all()

  def get_serializer_class(self):
   url = self.request.path
   if url == reverse("test1"):
     return ASerializer
   elif url == reverse("test2"):
    return CustomASerializer

urls.py

urlpatterns = [
  path('test1/', AView.as_view(), name="test1"),
  path('test2/', AView.as_view(), name="test2")
]
Related