Search code examples
djangodjango-middlewaredjango-cache

Exclude middleware from caching


I have very simple middleware that track numbers of "hits" of an object.

class HitCount():
    def process_view(self, request, view_func, view_args, view_kwargs):
        if request.resolver_match.url_name == 'article_view':
            try:
                Article.objects.filter(slug=view_kwargs['slug']).update(hit_count=F('hit_count')+1)
            except:
                pass

The problem is, it doesn't work properly when "per-site" cache is enabled.

"hit_count" field (PositiveIntegerField) is updated only once per 5 minutes (cache timeout 300s), no matter how many times page was visited during that "5 minutes". It doesnt have to be accurate, beacuse "hit_count" field isn't even displayed on front side. I need it only, to order articles via popularity. But "5 minutes measurement error" is too much. How can I exclude that middleware from caching? Do I need to disable cache for whole view?

settings file, I tried to change middleware order, but no effect.

MIDDLEWARE_CLASSES = (
    'django.middleware.cache.UpdateCacheMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'blog.middleware.HitCount',
    'django.middleware.cache.FetchFromCacheMiddleware',
)

view

class ArticleDetailView(DetailView):
    queryset = Article.objects.published()
    template_name = 'article.html'

Solution

  • The FetchFromCache middleware is fetching the page from the cache during process_request.

    That means that the process_view middleware is never called.

    You could try changing your middleware to override process_request instead. However, that might be tricky, because you won't have access to request.resolver_match.