Django QuerySets and cache

14,448

It depends where you are trying to optimize. Django caching (not to be confused with QuerySet caching which is an important aspect of their behavior to be conscious of) is used when you want to skip the dynamic part of serving up a dynamic page. So the main thought when using those is will (and for how long) the content I present to the user not change? In terms of optimizing your call into products, functools.lru_cache will give you a simple in-memory cache but your main concern will be how often to bust the cache with cache_clear(). To fully answer your question:

Caching typically won't hurt but it may also not be necessary if you aren't noticing performance issues. If you are worried about not hammering the database so much when using products(), you can leverage lru_cache and you'll need to decide when/if to cache bust which is informed by the nature of your data. That being said, databases are highly optimized for retrievals like this, and it may not be worth the extra logic depending on your application. Plus, you'll want to consider how much stuff that was in persistent store you are now keeping in-memory and if that trade-off is worth it.

If you are constantly regenerating the same pages to present to users, look into using Django caching. Skipping the steps prior to products() as well can only help if they are indeed stable for some period of time.

Personally, I would just leave lru_cache alone (and any idea of adding extra caching directly to your db query) based on what you have described. This sort of in-memory caching can be helpful for things that are accessed often and have to be computed a single time, calls that are very expensive yet yield the same result(s), situations where you have to call external APIs that have fees/are unreliable, etc. I would cache toward the presentation layer if it seems like you are constantly presenting the same page(s) to users before heaping complexity in front of the ORM - you probably want your managers to actually interact with your models as expected when called.

Followup 12-10-16

Since this answer I have had the opportunity to use django cachalot and have found it works very well if you decide you do want to cache database queries. It requires little effort to setup and has a wonderful exploration of the trade-offs in the documentation.

Followup 08-05-20

django_cachalot is not compatible with latest Django currently (3.1+) so I would not still recommend it nor do I currently use it. If you need heavier ORM caching, django_cacheops is current and viable.

Share:
14,448
Egor Biriukov
Author by

Egor Biriukov

Passionate and dedicated Senior Software Engineer with over 7 years of experience in solving problems and building products across various business domains (e-commerce/infrastructure/biotech/cloud/...) and technological stacks (Python/Golang/Ruby/C++/C#/Erlang/TypeScript/...). I'm interested in distributed systems, software architecture, reducing complexity, building meaningful products and inventing elegant solutions for complex problems.

Updated on June 04, 2022

Comments

  • Egor Biriukov
    Egor Biriukov almost 2 years

    I have a method in some of my Object Managers, that fetches data from db:

    def products(self, offset=None)
    

    if there is no offset passed, it simply returns all objects (approximately 5000, not so much). If there is offest it returns, well, objects with offset.

    This method used a lot during application life-cycle. I guess it's reasonable to somehow cache its results.

    My question is: will functools.lru_cache do the job or should I consider using Django's caches? Or maybe I should just not to think about it prematurely?

  • Adam Barnes
    Adam Barnes almost 4 years
    I'd like to note that the project upon which I'm currently working had cachalot on it when I started, and there were also numerous incredibly hard to reproduce bugs to do with database queries. We went as far as moving database provider from self-hosting to managed, solving nothing. When I learnt what cachalot was doing, I removed it immediately, and not only did that class of bug disappear, the majority of our (poorly written) views actually sped up.
  • Jmills
    Jmills almost 4 years
    Since this followup, I've actually not used cachalot again since it is not compatible with newer versions of Django. I have used django-cacheops, which requires a more concerted approach and configuration, and has its own quirks (particularly in the admin). It does less "magic" than cachalot. I don't imagine I could/would use cachalot again as all projects I work on try to stay on the latest stable and secure Django. Thanks for your note, I'll amend the answer since it has been quite some time.