Adrien Lemaire
Adrien Lemaire

Reputation: 1894

Proper usage of viewset queryset

According to Django REST framework documentation, the following two code snippets should behave identically.

class UserViewSet(viewsets.ViewSet):
    """
    A simple ViewSet for listing or retrieving users.
    """
    def list(self, request):
        queryset = User.objects.all()
        serializer = UserSerializer(queryset, many=True)
        return Response(serializer.data)

    def retrieve(self, request, pk=None):
        queryset = User.objects.all()
        user = get_object_or_404(queryset, pk=pk)
        serializer = UserSerializer(user)
        return Response(serializer.data)
class UserViewSet(viewsets.ModelViewSet):
    """
    A viewset for viewing and editing user instances.
    """
    serializer_class = UserSerializer
    queryset = User.objects.all()

But the way I interpret it, in the first case the query User.objects.all() is run with every api call, which in the second case, the query is run only once when the web server is started, since it's class variable. Am I wrong ? At least in my tests, trying to mock User.objects.all will fail, since the UserViewSet.queryset will already be an empty Queryset object by that time.

Someone please explain me why shouldn't the queryset class argument be avoided like pest and get_queryset be used instead ? Edit: replacing queryset with get_queryset makes self.queryset undefined in the retrieve method, so I need to use self.get_queryset() within the method as well...

Upvotes: 4

Views: 6799

Answers (2)

Abhimanyu Kaushal
Abhimanyu Kaushal

Reputation: 90

Adding to Aliva's answer and quoting from DRF viewset code, under get_queryset() method definition it states:

This method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests. You may want to override this if you need to provide different querysets depending on the incoming request.

Upvotes: 1

aliva
aliva

Reputation: 5720

  1. you are wrong, django queries are lazy so in both case the query will run at response time

from django docs:

QuerySets are lazy – the act of creating a QuerySet doesn’t involve any database activity. You can stack filters together all day long, and Django won’t actually run the query until the QuerySet is evaluated

  1. ModelViewSet provides other actions like delete and update you may need to add some limitations on them for user model (like checking permissions or maybe you don't like to let users simply delete their profiles)

  2. self.queryset is used for router and basename and stuff, you can ignore it and set basename in your router manually. it's not forced but I think it make my code more readable

note that usually def get_queryset is used when you want to do some actions on default queryset for example limit self.queryset based on current user. so if get_queryset is going to return .objects.all() or .objects.filter(active=True) I suggest using self.queryset to have a cleaner code

note2: if you decide to define get_queryset I suggest to also define self.queryset (if possible)

note3: always use self.get_queryset in your view method, even you didn't defined this method, you may need need to create that method later and if your view method are using self.queryset it may cause some issues in your code

Upvotes: 7

Related Questions