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.
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 that's coming from the request. since it is a get
method, i assume you didn't pass any data to that serializer so on response you 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)