overridden list method

I’m trying to override list method so it will get all post related to author, not to post id
views.py

@permission_classes([IsAuthenticated])
class PostView(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializers
    # authentication_classes = (TokenAuthentication,)
    # permission_classes = (IsAuthenticated,)

    def list(self, request, pk):
        post = Post.objects.filter(author=pk)
        if post:
            serializer = PostSerializers(post, many=True)
            return Response(serializer.data, status=status.HTTP_200_OK)
        else:
            response = {"message": "the author has no posts"}
            return Response(response, status=status.HTTP_404_NOT_FOUND)

models.py

class Post(models.Model):
    content = models.CharField(max_length=400, null=True, default=None)
    date = models.DateTimeField(auto_now=True)
    author = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=False
    )
    image = models.ImageField(blank=True, upload_to="images/", null=True, default=None)
    objects = PostManager()

the list method returning a post for specific id I sent as pk, not for author id,
I can write another method with get action to get posts that I want , but I really want to know why this is not working

Just to clarify, is it returning a post or a queryset containing one post?

Can we see your urls.py?

Is your list method definitely being called and not the superclasses method? Maybe print something to check.

urls.py

router = routers.DefaultRouter()
router.register("post", PostView)
router.register("comment", CommentView)
router.register("like", LikeView)
# router.register("user", UserView)

urlpatterns = [
    path("", include(router.urls)),
]

it must return a queryset with all post related to author id

I ask whether its returning a post or a queryset containing a post because this comment is ambiguous. The list method (from ModelViewSet and in your subclass) should always return a queryset so if it’s returning a single post then the list method is not being called.

I would try confirming that your list method is actually being called by printing some debug information to the console, eg:

    def list(self, request, pk):
        post = Post.objects.filter(author=pk)
        print("custom list method is called")
        if post:
            serializer = PostSerializers(post, many=True)
            return Response(serializer.data, status=status.HTTP_200_OK)
        else:
            response = {"message": "the author has no posts"}
            return Response(response, status=status.HTTP_404_NOT_FOUND)

It would also be useful to know what request you are making to test this functionality.

A quick and ugly way is to use filtering:

then you make a request like: https://api.example.com/articles/?author_id=12345

The best way to do this is to use nested routers and have an url structure like:

/authors/   <--- list of authors
/articles/   <--- this gives a list of articles

/authors/1234  <--- the author with the id 1234
/authors/1234/articles   <--- articles by the author with id 1234

github / drf-nested-routers for nested routers. Then your get_queryset will look like:

def get_queryset(self):
    queryset = Post.objects.all()
    if 'author_id' in self.kwargs:
        queryset = queryset.filter(
            author=self.kwargs.pop('author_id')
        )
    return queryset

In any case, you don’t need to override the list function.

I tried to confirm list method , it is not printing anything in the console, so I think it is not being called,
and it is returning only single post ,
http://127.0.0.1:8000/post/2/
this is my request,…

That’s because post/{id} gets routed to the “retrieve” function. You should read the first link I posted in my last reply.

1 Like