How use Django Cache in view without cache all page

3.6k views Asked by At

I trying to use Django Cache to make better my views. Works great, 400ms to 8ms is perfect. But when user access page for the first time, Django cache page with user info in header and when I try log out, page continue with user info.

I try use cache in template too, but isn't good, my problem come from view, so continue 400ms.

My settings.py

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
        'LOCATION': 'unique-snowflake',
    }
}

My view.py

@cache_page(60 * 15)
def list(request, tag_slug=None):
    page = request.GET.get('page')
    data = questions_controller.list_questions(request, tag_slug, None, page)
    if data:
        return render(request, 'questions/list.html', data)
    return page_not_found(request, "Page not found")
2

There are 2 answers

0
PVSK On BEST ANSWER

I faced the same problem with per-view caching. User info of the first cached user shows for all users. And I can't use Template caching as it is slow.

Best approach is to cache the final result of the view using low-level cache API. If the data is dynamic then use django-signals to clear the stale cached data. Tweak the below code to your requirement.

Views:

from django.core.cache import cache    
def sample(request):
        cached_data = cache.get_many(['query1', 'query2'])
        if cached_data:
            return render(request, 'sample.html', {'query1': cached_data['query1'], 'query2': cached_data['query2']})
        else:
            queryset1 = Model.objects.all()
            queryset2 = Model2.objects.all()
            cache.set_many({'query1': queryset1 , 'query2': queryset2 }, None)
            return render(request, 'sample.html', {'query1': queryset1 , 'query2': queryset2})

Models:

from django.db.models.signals import post_save
from django.core.cache import cache

@receiver(post_save, sender=Model1)
@receiver(post_save, sender=Model2)
def purge_cache(instance, **kwargs):
    cache.delete_many(['query1', 'query2'])

Hope this helps.

2
Franey On

Per-view caching will cache the entire view, so it's a good fit for something like a contact page, but it isn't a good fit for views that have dynamic content.

It sounds like template caching is what you'll need here. For parts of the template that can change, you can add an argument to the {% cache %} tag to uniquely identify it (from the Django docs):

{% load cache %}
{% cache 500 header request.user.username %}
    .. header for logged in user ..
{% endcache %}

Everything in the {% cache %} tag will now be cached per-user so you don't end up with a situation where one user is seeing another user's header.