59

I'm trying to find a way to cache the results of a query that won't change with frequency. For example, categories of products from an e-commerce (cellphones, TV, etc). I'm thinking of using the template fragment caching, but in this fragment, I will iterate over a list of these categories. This list is avaliable in any part of the site, so it's in my base.html file. Do I have always to send the list of categories when rendering the templates? Or is there a more dynamic way to do this, making the list always available in the template?

Marcio Cruz
  • 2,012
  • 1
  • 23
  • 30

2 Answers2

92

Pop your cached query into Django's cache:

from django.core.cache import cache

cache.set('key', queryset)

Then create a context processor to add the value of the cache to all templates:

# myproject/myapp/context_processors.py

from django.core.cache import cache

def cached_queries():
    return {'cache', cache.get('key')}

Then add your context processor in your Django settings file:

TEMPLATE_CONTEXT_PROCESSORS += (
    'myproject.myapp.context_processors.cached_queries'
)

Now you will be able to access the cache variable in all generic templates and all templates which have a requests context, which a template is given if this is done in the view:

return render_to_response('my_template.html',
                          my_data_dictionary,
                          context_instance=RequestContext(request))

When to Set the Cache

It depends on what is contained in the cache. However a common problem is that Django only really gets to execute Python whenever a page request is sent, and this is often not where you want to do this kind of work.

An alternative is to create a custom management command for a particular app. You can then either run this manually when necessary, or more commonly set this to run as a cron job.

To create a management command you must create a class decended from Command inside of a management/commands directory located inside of an app:

# myproject/myapp/management/commands/update_cache.py

from django.core.management.base import NoArgsCommand
from django.core.cache import cache

class Command(NoArgsCommand):
    help = 'Refreshes my cache'

    def handle_noargs(self, **options):
        cache.set('key', queryset)

The name of this file is important as this will be the name of the command. In this case you can now call this on the command line:

python manage.py update_cache
Marcus Whybrow
  • 19,578
  • 9
  • 70
  • 90
  • I didn't know about context processors before, that wat the missing key. What is the best place to set the cache? I'm thinking of extending the model's manager, so everywhere I need to get this list, I will avoid a hit in the database. – Marcio Cruz Jan 08 '11 at 13:16
  • Another way to do this is to cache just the page fragment where I iterate the list. – Marcio Cruz Jan 08 '11 at 15:19
  • @Marcio I have updated the end of my answer to include **When to Set the Cache**. It suggests using a Django management command to to this work, and then using a cron job to run it automatically every so often (maybe once a day) – Marcus Whybrow Jan 08 '11 at 23:43
  • Nice answer. An other elegant way to update the cache is to **override the model's save method** [like here](https://stackoverflow.com/questions/4269605/django-override-save-for-model) to include the `cache.set()` – zar3bski May 29 '19 at 14:23
  • Very cool answer, in addition you will need to set your cache binding preferences in the ```settings.py``` file using the ```CACHES``` setting, see more info [here](https://docs.djangoproject.com/en/3.0/topics/cache/) – Rui Vaz Apr 09 '20 at 23:02
16

You can also use johnny-cache for automatic caching of querysets. It will (by default) cache all querysets, but you can force it not to cache some.

Matthew Schinckel
  • 35,041
  • 6
  • 86
  • 121
  • 21
    Johnny-cache isn’t supported anymore, use [django-cachalot](http://django-cachalot.readthedocs.io/en/latest/) instead. – Soitje Oct 24 '16 at 16:02