I am defining a ModelViewSet
using django-rest-framework
.
I need to override the default queryset to perform some processing on the queryset objects before rendering the response.
This process is time-expensive so I would like to execute it only on the objects that will be actually available to the consumer due to the paginated response, instead of applying this process to ALL the objects and applying pagination AFTER finishing my processing, which I can notice (correct me if I'm wrong) is the default behavior in DRF.
In short what I need is:
If the default queryset is 1000 objects
, but the pagination is restricted to 25 objects per page
, I want to apply my process only those 25 objects
. Please note there is no other constraints for reducing the final amount of objects other than pagination.
Is there a way to do this?
Is overriding the default queryset a bad idea in this case?
Thanks!
Theory:
As it has been stated before:
Django querysets are lazy.
They only hit the database when they absolutely need to (like when you are doing to do the processing before the query and pagination.).
There are two parts on DRF's pagination process:
paginate_queryset
get_paginated_response
We can choose which part to override, depending on our needs (See the Practice part)
Practice:
Depending on what you really want to process, there are two options.
For this I assume that we are going to extend/override the
LimitOffsetPagination
class, which is easier for an example, but the same principles apply to every other DRF pagination.Processing the model objects:
If you want the preprocessing to be executed on the model objects and be permanent on your database, you need to override the
paginate_queryset
method:Processing the paginated response:
If you want the preprocessing to be executed on the response and NOT to be permanent on your database, you need to override the
get_paginated_response
method:There is no "easy" way to do that. In Django REST framework pagination is done in the same method as rendering.
So I guess the best way to go is to define your own Viewset and redeclare the list method: