HenryM
HenryM

Reputation: 5793

How to cache view for all users in Django

I'm trying to use a Memcached instance of AWS ElastiCache with a Django project. It seems to be caching a view for a user, but if you come in on a different PC, it isn't cached until called from that PC (or same PC with different browser).

I'm not sure what I've got wrong.

Within settings.py I have

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': os.environ.get('CACHE_LOCATION','127.0.0.1:11211'),
    }
}

MIDDLEWARE = [
    'core.middleware.DenyIndexMiddleware',
    'core.middleware.XForwardedForMiddleware',
    'core.middleware.PrimaryHostRedirectMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.cache.UpdateCacheMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django.contrib.redirects.middleware.RedirectFallbackMiddleware',
    'masquerade.middleware.MasqueradeMiddleware',
    'django.middleware.locale.LocaleMiddleware',
    'django.contrib.sites.middleware.CurrentSiteMiddleware',
    'cms.middleware.user.CurrentUserMiddleware',
    'cms.middleware.page.CurrentPageMiddleware',
    'cms.middleware.toolbar.ToolbarMiddleware',
    'cms.middleware.language.LanguageCookieMiddleware',
    'cms.middleware.utils.ApphookReloadMiddleware',
    'django.middleware.cache.FetchFromCacheMiddleware',
]

I've then cached the views using cache_page

path('<str:service_type>/<str:location>/', cache_page(60*60)(views.canonical_search), name="canonical-search"),

How do I cache the site so that the page is cached irrespective of the user?

EDIT I've noticed that it never caches when using the user is logged in.

Upvotes: 5

Views: 1924

Answers (3)

Benjamin RD
Benjamin RD

Reputation: 12044

Though Django documentation, you can read this: Django’s cache framework

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': '127.0.0.1:11211',
    }
}

If you need to store a page in the local

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
        'LOCATION': 'c:/foo/bar',
    }
}

But, my recommendation is store the results (database result, assets, etc), as @PVSK show in this thread:

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})

Upvotes: 2

ACimander
ACimander

Reputation: 1979

Hm, at first I wondered if you're running into some cache default limitations. You are not using OPTIONS in your CACHE backend definition, so the cache is limited to 300 entries per default.

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': os.environ.get('CACHE_LOCATION','127.0.0.1:11211'),
        'OPTIONS': {
            'MAX_ENTRIES': 1000
        }

    }
}

The next possible problem, that we also had, is that the cache_key generation takes the full QUERY_STRING into account, so (the ?param=bla). But you already stated that the url is the same for all users.

Next up, as SebCorbin correctly identified, are the potential Vary issues.

UpdateCacheMiddleware will never cache a cookie-setting response to a cookie-less request.

    def process_response(self, request, response):
        #...

        # Don't cache responses that set a user-specific (and maybe security
        # sensitive) cookie in response to a cookie-less request.
        if not request.COOKIES and response.cookies and has_vary_header(response, 'Cookie'):
            return response

        # ...

The order of execution of middlewares is top-to-bottom for process_request and bottom-to-top for process_response.

I suspect that one of lower middlewares (or the view) is setting a cookie, so you might be able to work around this issue by moving the 'django.middleware.cache.UpdateCacheMiddleware' below the offending middleware, but you risk loosing features if you don't move feature-middlewares like LocaleMiddleware as well.

If your view code is setting cookies you'll need to switch to the low-level cache API to cache costly operations (or move the cookie logic into a middleware that lives above the UpdateCacheMiddleware middleware).

Upvotes: 0

SebCorbin
SebCorbin

Reputation: 1733

Watch out for the Vary header, that cache_page() takes into account.

Usually, some middlewares may add a Vary header, for example :

  • CsrfViewMiddleware adds Cookie,
  • GZipMiddlewareadds Accept-Encoding
  • LanguageCookieMiddleware may add Accept-Language

meaning that as soon as you have a different Cookie (session), encoding, or language, you have a different version of cache for your page.

As for you case, the CsrfViewMiddleware may be the problem, you can add the decorator @csrf_exempt to your view so that the Vary: Cookie header is not set in the response.

More info at https://docs.djangoproject.com/en/3.0/topics/cache/#using-vary-headers

Upvotes: 5

Related Questions