Home > Blockchain >  Django REST Framework - Custom action to use different serializer class, empty result
Django REST Framework - Custom action to use different serializer class, empty result

Time:12-03

I'm trying to define a custom action which will use a different serializer class, but I'm getting an empty result from that action and I'm not sure why. I've based my action definition code on the DRF documentation, but there may be something that I'm missing, since this is new to me.

I have a simple example based on Authors & Books to demonstrate this.

Here is my viewset code, and the action where I think the solution may lie:

class AuthorViewSet(viewsets.ModelViewSet):
    queryset = Author.objects.all()
    serializer_class = AuthorSerializer
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['first_name', 'last_name']

    @action(detail=False, serializer_class=AuthorNoBookListSerializer)
    def no_booklist(self, request):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            return Response(serializer.data)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

I have two serializers: One with each author's list of books, and one without:

class AuthorSerializer(serializers.ModelSerializer):
    book_list = serializers.ListField(source='books')

    class Meta:
        model = Author
        fields = ('id', 'first_name', 'last_name', 'book_list')

class AuthorNoBookListSerializer(serializers.ModelSerializer):
    class Meta:
        model = Author
        fields = ('id', 'first_name', 'last_name')

If I swap out serializer_class = AuthorSerializer for serializer_class = AuthorNoBookListSerializer in the viewset, I can see that both serializers work fine:

Using AuthorSerializer:

HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "count": 4,
    "next": null,
    "previous": null,
    "results": [
        {
            "id": 1,
            "first_name": "Ken",
            "last_name": "Kesey",
            "book_list": [
                "One Flew Over the Cuckoo's Nest"
            ]
        },
        {
            "id": 2,
            "first_name": "Brian",
            "last_name": "Kernighan",
            "book_list": [
                "The C Programming Language"
            ]
        },
        {
            "id": 3,
            "first_name": "Dennis",
            "last_name": "Ritchie",
            "book_list": [
                "The C Programming Language",
                "Lions' Commentary on UNIX: 6th Edition with Source Code"
            ]
        },
        {
            "id": 4,
            "first_name": "John",
            "last_name": "Lions",
            "book_list": [
                "Lions' Commentary on UNIX: 6th Edition with Source Code"
            ]
        }
    ]
}

With AuthorNoBookListSerializer:

HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "count": 4,
    "next": null,
    "previous": null,
    "results": [
        {
            "id": 1,
            "first_name": "Ken",
            "last_name": "Kesey"
        },
        {
            "id": 2,
            "first_name": "Brian",
            "last_name": "Kernighan"
        },
        {
            "id": 3,
            "first_name": "Dennis",
            "last_name": "Ritchie"
        },
        {
            "id": 4,
            "first_name": "John",
            "last_name": "Lions"
        }
    ]
}

But using the action (at http://localhost:8000/api/authors/no_booklist/) I get an empty response:

HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{}

I think my action definition is somehow incorrect. I'm not sure if I'm creating the Response correctly. Any insight is appreciated.

CodePudding user response:

As you said the problem is not the serializer class. Your problem lies here @action(detail=False, serializer_class=AuthorNoBookListSerializer)

The action decorator needs methods kwarg and your problem would be solved.

@action(methods = ["get"], detail=False, serializer_class=AuthorNoBookListSerializer)

Otherwise the method as_view() can not map action to methods.

The other issue is with this line serializer = self.get_serializer(data=request.data)

The serializer only have the data thats coming from the request. since it a get method, i assume u didnt pass any data to that serializer so on responce u get empty dict. Instead pass the queryset as well

     ...
     serializer = self.get_serializer(instance = self.queryset, many = True)
     # remove the validity check here
     return Response(serializer.data, status=...)
     ...

To get the paginated response u can either filter and paginate the queryset here. but since its Implemented on super().list() method u can return that.

@action(detail=False, serializer_class=AuthorNoBookListSerializer)
    def no_booklist(self, request, *args, **kwargs):
        return super().list(request, args, kwargs)

  • Related