Question

This problem has been bugging me all morning:

  • I have some views which accept GET params to modify the queryset (order_by and pagination) and cache it.
  • These views are very similar and they all share the same template.
  • These template caches a part of the page (taking the GET params into account) like this:

    {% with order=request.GET.order_by %}{% with page=request.GET.page %}{# I need them to set the cache name #}
    {% cache 7200 template urlname order page %}
    .... some part of the page...
    {% endcache %}
    {% endwith %}{% endwith %}{# with order and with page #}
    
  • Deleting the queryset cache is a piece of cake, but deleting the template cache has proven to be quite complicated. You usually need to generate the cache_key, like explained in the DOCS. The problem in my case is that I have to generate all the possible combinations of keys and delete them, which I'm doing like this:

    SITE_LIST_OPTIONS = [
        ('url', [reverse_lazy('site_list'),
                 reverse_lazy('best_site_list'),
                 reverse_lazy('banned_site_list'),
                 reverse_lazy('toreview_site_list')]),
        ('order', ['url', 'country', 'upstream_rank']),
        ('page', range(10))]
    
    
    def delete_cache_keys(keys):
        '''Deletes all the cache keys on the provided list.'''
        for e in keys:
            cache.delete(e)
    
    
    def delete_template_cache(key, filters=None):
        # first, we need to get all possible filter combination options
        if filters:
            options = combine_options(filters)
            keys = [make_template_fragment_key(key, ops) for ops in options]
            delete_cache_keys(keys)
        else:
            key = make_template_fragment_key(key, filters)
            cache.delete(key)
    
    # context_processor.py (used to generate key in template)
    
    def urlname(request):
        return {'urlname': resolve_urlname(request)}
    

I imagine I'm not the first person to ever try to cache templates based on GET params, so... is there a saner way to delete all the caches related to this particular template?

Edit: not sure why the formatting isn't showing up but here's a cleaner version just in case http://dpaste.org/XYYo2/

Was it helpful?

Solution

You could group all cached snippets of a template, together by giving them a group cache key bound to the template. When you want to invalidate all of contents related to the template, you just clear the group key of the template:

>>> from django.core.cache import cache

>>> template_name = 'foo.html'
>>> cache.set(template_name, 1)
>>> make_group_key = lambda: '{name}-{version}'.format(
        name=template_name, version=cache.get(template_name))
>>> cache.set(make_group_key(), 'page content...')
>>> cache.get(make_group_key())
'page content...'

>>> cache.incr(template_name)
>>> cache.get(make_group_key()) is None
True

For your code, you could write a template tag which propagates the group key to context, like

{% get_template_key template as tmpl_key %}
{% cache 7200 template urlname order page tmpl_key %}

or you could generate the key in the view or somewhere else.

The delete part is then as simple as cache.incr(template_name) (You may need to deal w/ the exception that template_name no longer exists)

This method add an extra cache round-trip, but normally its fast enough.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top