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.
Metadata
Release files for django-cache-memoized 0.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| django_cache_memoized-0.3.tar.gz | 12.4 kB | Without provenance | Details for django_cache_memoized-0.3.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| django_cache_memoized-0.3-py3-none-any.whl | Python 3 | none | any | Details for django_cache_memoized-0.3-py3-none-any.whl |
Total release size: 25.0 kB
Release files / django_cache_memoized-0.3.tar.gz
| Download URL | django_cache_memoized-0.3.tar.gz |
|---|---|
| Size | 12.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
802c6ba2f333292e0cb7a392abf06178dd64b542681f2bb6af757056539123a5
|
|
BLAKE2b-256 checksum How to use checksums |
205c7ebfec35bd5115bd36a1fe4ec66a71c07d5cc190efdd4bdec34a4367ab43
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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
|
Release files / django_cache_memoized-0.3-py3-none-any.whl
| Download URL | django_cache_memoized-0.3-py3-none-any.whl |
|---|---|
| Size | 12.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e9cfc908d86db0d220bf5a4661dd77680a89c56d5b5f856b5e37f93f9c4eb10c
|
|
BLAKE2b-256 checksum How to use checksums |
b97d6004b02027f7445baef24c56f7658c733e166b011c8d395f85b562f20f6e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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
|
Security for django-cache-memoized 0.3
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.