Improving the performance of your Django project

The first required quote from Sir Tony Hoare on this topic is as follows:

"premature optimization is the root of all evil."

Plenty of times I have have seen someone worry about optimisation of a page before it has even hit production is worrying. That isn't to say you shouldn't avoid obvious N+1 issues or similar known problems. But I do want to emphasis that a shipped non-optiminal page is better than page never shipped.

This leads me to my second point. You need to be able measure performance before you can know what is slow and what is fast. My recommendations are using Django Debug Toolbar for debugging performance locally and something like Sentry, (Scout)[] or django-silk are excellent for hosted production environments. They all will highlight timings and various details about where code is slow and causing your users pain.

Finally there is the actual optimisation work. Here the solution is most often the classic 'It Depends'. That said, in my recent travels through a client project which was (and still is) riddled with N+1 issues, here are some common places to check for optimisations:

  • Iterating over a queryset in a view and adding items to a python list
  • Having custom template tags with queries in them, if they are called inside a template {% for %} loop tag
  • Nested for loops in a view or in a template
  • Calling model methods inside a for loop in the template (this often hides a query or three)

I find the general solution here is to annotate the initial queryset with the required data so to flatten out any queries into a larger initial query. It won't work all the time but it's a starting point.

Finally other possible optimisations are background workers like celery, to use async views, or JS libraries like HTMX to progressive load a page.

I am sure I have missed something obvious so give me a shout on socials!