12

I have this model:

class Article(models.Model):
    title = models.CharField(max_length=300, blank=False)
    body = models.TextField(max_length=10000, blank=False)
    created = models.DateTimeField(auto_now_add=True)


    def last_post(self):
        if self.post_set.count():
            return self.post_set.order_by("-created")[0]

I have noticed that the last_post creates a very costly and frequently run query. So I want to cache it for 5 minutes.

I know how to cache a queryset in views but last_post bypasses views and is directly called in template. So appreciate your hints on how to cache it.

Jand
  • 2,527
  • 12
  • 36
  • 66
  • 1
    Maybe `cached_property` is what you need. Look at: http://www.pydanny.com/cached-property.html – Alex Yu Dec 09 '15 at 21:35
  • Sounds good. Can you elaborate it as a complete answer please? – Jand Dec 09 '15 at 21:45
  • `@cached_property` will cache the result for the whole request, not for 5 minutes. That perhaps works for you. – Lorenzo Peña Dec 09 '15 at 21:55
  • 3
    Note that a `cached_property` decorator is now available in Django: https://docs.djangoproject.com/en/1.9/ref/utils/#django.utils.functional.cached_property – Thomas Druez Dec 09 '15 at 22:03

2 Answers2

8

I suppose that you can use cached_property_with_ttl from https://pypi.python.org/pypi/cached-property/1.2.0

from cached_property import cached_property_with_ttl

class Article(models.Model):
    title = models.CharField(max_length=300, blank=False)
    body = models.TextField(max_length=10000, blank=False)
    created = models.DateTimeField(auto_now_add=True)

    @cached_property_with_ttl(ttl=5)
    def last_post(self):
        if self.post_set.count():
            return self.post_set.order_by("-created")[0]

Hope this will work for you.

Alex Yu
  • 3,412
  • 1
  • 25
  • 38
2

EDIT : @Yassine Belmamoun pointed out that this won't work because the instance dies with the request. I'm keeping it so people have an example of what NOT to do.

Original answer (DO NOT USE):

As @Thomas Druez said, Django now has a built-in cached_property:

from django.utils.functional import cached_property

class Article(models.Model):

    @cached_property
    ## DON'T USE THIS; IT DOES NOT WORK
    ## KEEPING AS REFERENCE OF WHAT NOT TO DO
    def last_post(self):
        if self.post_set.count():
            return self.post_set.order_by("-created")[0]
pianoJames
  • 454
  • 1
  • 5
  • 14
  • 5
    The `cached_property` decorator "will persist as long as the instance does". This means that the result will be computed for each new request. This won't work. – Yassine Belmamoun Jul 16 '20 at 04:33