KVISH
KVISH

Reputation: 13178

cache_page with Class Based Views

I'm trying to do cache_page with class based views (TemplateView) and i'm not able to. I followed instructions here:

Django--URL Caching Failing for Class Based Views

as well as here:

https://github.com/msgre/hazard/blob/master/hazard/urls.py

But I get this error:

cache_page has a single mandatory positional argument: timeout

I read the code for cache_page and it has the following:

if len(args) != 1 or callable(args[0]):
    raise TypeError("cache_page has a single mandatory positional argument: timeout")
cache_timeout = args[0]

which means it wont allow more than 1 argument. Is there any other way to get cache_page to work?? I have been digging into this for sometime...

It seems like the previous solutions wont work any longer

Upvotes: 42

Views: 26717

Answers (9)

Uzzal H. Mohammad
Uzzal H. Mohammad

Reputation: 801

Would like to add this: If you need to use multiple decorators for cache like vary_on_headers and cache_page together, here is one way I did:

class CacheHeaderMixin(object):
    cache_timeout = int(config('CACHE_TIMEOUT', default=300))
    # cache_timeout = 60 * 5

    def get_cache_timeout(self):
       return self.cache_timeout

    def dispatch(self, *args, **kwargs):
       return vary_on_headers('Authorization')(cache_page(self.get_cache_timeout())(super(CacheHeaderMixin, self).dispatch))(*args, **kwargs)

This way cache is stored and it varies for different Authorization header (JWT). You may use like this for a class based view.

class UserListAPIView(CacheHeaderMixin, ListAPIView):
    serializer_class = UserSerializer
    def get_queryset(self):
        return CustomUser.objects.all()

Upvotes: 3

Alasdair
Alasdair

Reputation: 308809

According to the caching docs, the correct way to cache a CBV in the URLs is:

from django.views.decorators.cache import cache_page

url(r'^my_url/?$', cache_page(60*60)(MyView.as_view())),

Note that the answer you linked to is out of date. The old way of using the decorator has been removed (changeset).

Upvotes: 64

Andreas Bergström
Andreas Bergström

Reputation: 14610

You can add it as a class decorator and even add multiple using a list:

@method_decorator([vary_on_cookie, cache_page(900)], name='dispatch')
class SomeClass(View):
   ...

Upvotes: 13

Phil Gyford
Phil Gyford

Reputation: 14594

Here's my variation of the CachedView() mixin - I don't want to cache the view if the user is authenticated, because their view of pages will be unique to them (e.g. include their username, log-out link, etc).

class CacheMixin(object):
    """
    Add this mixin to a view to cache it.

    Disables caching for logged-in users.
    """
    cache_timeout = 60 * 5 # seconds

    def get_cache_timeout(self):
        return self.cache_timeout

    def dispatch(self, *args, **kwargs):
        if hasattr(self.request, 'user') and self.request.user.is_authenticated:
            # Logged-in, return the page without caching.
            return super().dispatch(*args, **kwargs)
        else:
            # Unauthenticated user; use caching.
            return cache_page(self.get_cache_timeout())(super().dispatch)(*args, **kwargs)

Upvotes: 0

epicwhale
epicwhale

Reputation: 1873

You can simply decorate the class itself instead of overriding the dispatch method or using a mixin.

For example

from django.views.decorators.cache import cache_page
from django.utils.decorators import method_decorator

@method_decorator(cache_page(60 * 5), name='dispatch')
class ListView(ListView):
...

Django docs on decorating a method within a class based view.

Upvotes: 37

madjardi
madjardi

Reputation: 5929

yet another good example CacheMixin from cyberdelia github

class CacheMixin(object):
    cache_timeout = 60

    def get_cache_timeout(self):
        return self.cache_timeout

    def dispatch(self, *args, **kwargs):
        return cache_page(self.get_cache_timeout())(super(CacheMixin, self).dispatch)(*args, **kwargs)

usecase:

from django.views.generic.detail import DetailView


class ArticleView(CacheMixin, DetailView):
    cache_timeout = 90
    template_name = "article_detail.html"
    queryset = Article.objects.articles()
    context_object_name = "article"

Upvotes: 32

Kevin Parker
Kevin Parker

Reputation: 17206

Yet another answer, we found this to be simplest and is specific to template views.

class CachedTemplateView(TemplateView):
    @classonlymethod
    def as_view(cls, **initkwargs): #@NoSelf
        return cache_page(15 * 60)(super(CachedTemplateView, cls).as_view(**initkwargs))

Upvotes: 3

Krystian Cybulski
Krystian Cybulski

Reputation: 11108

I created this little mixin generator to do the caching in the views file, instead of in the URL conf:

def CachedView(cache_time=60 * 60):
    """
    Mixing generator for caching class-based views.

    Example usage:

    class MyView(CachedView(60), TemplateView):
        ....

    :param cache_time: time to cache the page, in seconds
    :return: a mixin for caching a view for a particular number of seconds
    """
    class CacheMixin(object):
        @classmethod
        def as_view(cls, **initkwargs):
            return cache_page(cache_time)(
                super(CacheMixin, cls).as_view(**initkwargs)
            )
    return CacheMixin

Upvotes: 3

Alexander Artemenko
Alexander Artemenko

Reputation: 22776

I didn't found a good cache solution for class based views and created my own: https://gist.github.com/svetlyak40wt/11126018

It is a mixin for a class. Add it before the main base class and implement method get_cache_params like that:

def get_cache_params(self, *args, **kwargs):
   return ('some-prefix-{username}'.format(
       username=self.request.user.username),
            3600)

Upvotes: 2

Related Questions