Skip to main content

Django decorator that can memoize any function or method to the configured Django cache

Project description

Django Cache Memoized

Django is one of the most popular Python web frameworks today. Importantly it provides an ORM permitting us to define models as Python classes that Django maps to a database representation for us.

it supports a configurable Caching system as well to boost performance significantly (rendering pages from cache rather than rebuilding them from database queries).

Alas it does not provide easy access to the arbitrary memoization of functions and methods using that cache. And while it does provide a cached_property decorator, it implements that using a dictionary which has the lifetime of a request typically and it only applies to methods.

Here we provide a new decorator which will decorate an arbitrary function or method, and with which you can specify a caching key.

Memoization and Cache keys

A brief introduction is warranted. To memoize a function means to cache the result that it returns and on subsequent calls to that function with the same arguments, rather than recalculate, the result, return the previously cached result.

These results are stored in a cache with an key that can be used to retied the result later from the same cache. The key must identify the function and its arguments uniquely, and be unique to this function and the arguments it was called with, for the cached result to be of any use.

While keys can be generated automatically to try and achieve that, Django Cache Memoized aloows you to define one flexibly as well.

How to use

Firstly, configure Django caching. Without caching configured it makes no sense to memoize function to the cache.

Once configured you can memoize any function or method as follows. Three styles are supported:

from django_cache_memoized import memoized

@memoized
def my_function(arg1, arg2 ...)
    ...
    return result

This will cache result against a default key, which is generated from the name of the function, the class (if it is a method) and the arguments it was called with.

from django_cache_memoized import memoized

@memoized()
def my_function(arg1, arg2 ...)
    ...
    return result

The same deal really. Just has optional and meaningless parentheses is all.

from django-cache-memoized import memoized

@memoized("a key pattern")
def my_function(arg1, arg2 ...)
    ...
    return result

That is with a key pattern specified. The pattern is simply an f-string, crucially without the f prefix. It can reference any of the arguments of the to-be-memoized function. For example a sample key pattern for the sample function above might be "my_function({arg1}, {arg2})".

The f-string will be evaluated inside the decorated function when with the actually values of arg1 and arg2 etc that the function was called with.

Finally, if the first argument is named 'self' then the function assumed to be a class method. If it has an attribute 'pk' (for primary key) it is further assumed to be a method on a Django model instance.

But you can force it to treat the function as a method with one optional argument:

from django_cache_memoized import memoized

@memoized("a key pattern", method=True)
def my_function(arg1, arg2 ...)
    ...
    return result

To be honest all that does, is impact the default key generator used. That is all really.

Performance boost

I wrote this because of particular need. A Django view that presented summary data to which I added drill down using a <details> tag. The details though were easiest to generate by querying the relevant models. Doing that in the template once per row in a table had a fairly hefty impact on load time. My one page was generating 4000 queries on load. One hefty table!

So I was faced with some serious head scratching query generation, joining a pile of tables and aggregating lists somehow (some of which I did do) and/or memoizing ... And many of those 4000 queries were repeats, certainly on revisits tot he page but even within the page.

And I simply could not find a flexible memoizer of any kind let alone one that used the configured Django cache. So I wrote one.

That one page had a load time of about 7 seconds (and about 3000 queries) before memoization and with first cut memoization (i.e. just one one bottle-necked template tag I have) it dropped to under 0.7 seconds (and 7 queries). Still too many but I wanted to illustrate just how eye-opening the first @memoized decoration's impact was. It only gets better.

The catch

There's a catch of course. And one you will have to be aware of and manage. But this is true of all memoization. That is you need to be aware of the life cycle of the Django cache - it's different with different configurations but essentially cached items only live for so long, they typically expire at some point. That's not generally an issue unless it's too short and you're not getting a lot of benefit across requests and visits from the cache. The bigger problem to be aware of is the validity of the cached result. For example if you're caching model instance properties at all (and why not?! if it makes sense to) then if that model instance is edited and saved that might invalidate some of the cached results. Meaning you need to be aware when saving data to the data base that invalidating some cache entries is important. Failing to do so means pages using memoized results won't see the edits saved to the database.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_cache_memoized-0.3.tar.gz (12.4 kB view details)

Uploaded Source

Built Distribution

django_cache_memoized-0.3-py3-none-any.whl (12.7 kB view details)

Uploaded Python 3

File details

Details for the file django_cache_memoized-0.3.tar.gz.

File metadata

  • Download URL: django_cache_memoized-0.3.tar.gz
  • Upload date:
  • Size: 12.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.8.0 pkginfo/1.8.2 readme-renderer/32.0 requests/2.27.1 requests-toolbelt/0.9.1 urllib3/1.26.8 tqdm/4.63.0 importlib-metadata/4.11.2 keyring/23.5.0 rfc3986/2.0.0 colorama/0.4.4 CPython/3.8.10

File hashes

Hashes for django_cache_memoized-0.3.tar.gz
Algorithm Hash digest
SHA256 802c6ba2f333292e0cb7a392abf06178dd64b542681f2bb6af757056539123a5
MD5 8da85c65a8f1146485e5eafa4207cb36
BLAKE2b-256 205c7ebfec35bd5115bd36a1fe4ec66a71c07d5cc190efdd4bdec34a4367ab43

See more details on using hashes here.

File details

Details for the file django_cache_memoized-0.3-py3-none-any.whl.

File metadata

  • Download URL: django_cache_memoized-0.3-py3-none-any.whl
  • Upload date:
  • Size: 12.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.8.0 pkginfo/1.8.2 readme-renderer/32.0 requests/2.27.1 requests-toolbelt/0.9.1 urllib3/1.26.8 tqdm/4.63.0 importlib-metadata/4.11.2 keyring/23.5.0 rfc3986/2.0.0 colorama/0.4.4 CPython/3.8.10

File hashes

Hashes for django_cache_memoized-0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 e9cfc908d86db0d220bf5a4661dd77680a89c56d5b5f856b5e37f93f9c4eb10c
MD5 5c4af30dd2afb19d081963d636b7deb1
BLAKE2b-256 b97d6004b02027f7445baef24c56f7658c733e166b011c8d395f85b562f20f6e

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page