DRF page number paginator performance¶
There’s often the case where we encounter applications which require paginated lists of data and of course being a very common pattern, Django and DRF already provide solutions for dealing with this. We would simply hook one of DRF’s built in paginator classes and be done with it. However as our application grows we start adding new properties, foreign relationships by the dozens to our model and all of a sudden our response time starts to degrade1.
So, what happened?¶
Without getting into much detail, Django’s orm is doing its job of building a database query behind the scenes for you. However in doing so, it usually fetches each and every property of your model and all related models which are being selected in that query, which translated to the actual database call causes a lot of overhead.
Ehm… how can I fix this?¶
Well as it turns out you could use
Django’s
built in queryset methods .only
or .defer
to specify only the properties of
your model and related models to avoid this problem. You could end up with
something like this:
1 2 3 4 5 6 |
|
Well you get the idea… However the above solution will get ugly very fast when we are talking about 10+ properties, it’s just not going to scale for our specific use case.
Then there’s nothing we can do¶
Well, actually there’s one solution which has proven very effective to me. Let’s recap for a second, we are suffering due to having many columns being selected in our query but we need them and having to specify them all for each one of our models which have these issues is not going to be pretty.
So how about if we could fetch our models without columns first, build a query with their pk value only and then make our final query to fetch our models fully?.
Show me the code already!!!¶
Below is my current solution2 to this situation. I’ve added comments to each relevant step, the rest is just Django’s code which needs to be there.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
|
And there you go
Downsides¶
Each request will now issue 3 database queries to your database as opposed of the 2 it normally does. However the performance gains out weight this stat 1
-
Always measure before making any assumptions. Django’s debug toolbar is an excellent tool for this purpose ↩↩
-
Tested against Django 1.10.x, 1.11.x and DRF 3.6.x ↩