Copyright Peter Bengtsson, mail@peterbe.com, 2013-2022
License: BSD
A Django cache_page
decorator on steroids.
Unlike the stock django.views.decorators.cache.change_page
this
decorator makes it possible to set a key_prefix
that is a
callable. This callable is passed the request and if it returns None
the page is not cached.
Also, you can set another callable called post_process_response
(which is passed the response and the request) which can do some
additional changes to the response before it's set in cache.
Lastly, you can set post_process_response_always=True
so that the
post_process_response
callable is always called, even when the
response is coming from the cache.
In your Django views:
from fancy_cache import cache_page
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView
@cache_page(60 * 60)
def myview(request):
return render(request, 'page1.html')
def prefixer(request):
if request.method != 'GET':
return None
if request.GET.get('no-cache'):
return None
return 'myprefix'
@cache_page(60 * 60, key_prefix=prefixer)
def myotherview(request):
return render(request, 'page2.html')
def post_processor(response, request):
response.content += '<!-- this was post processed -->'
return response
@cache_page(
60 * 60,
key_prefix=prefixer,
post_process_response=post_processor)
def yetanotherotherview(request):
return render(request, 'page3.html')
class MyClassBasedView(TemplateView):
template_name = 'page4.html'
@method_decorator(cache_page(60*60))
def get(self, request, *args, **kwargs):
return super().get(request, *args, **kwargs)
If you want to you can have django-fancy-cache
record every URL it
caches. This can be useful for things like invalidation or curious
statistical inspection.
You can either switch this on on the decorator itself. Like this:
from fancy_cache import cache_page
@cache_page(60 * 60, remember_all_urls=True)
def myview(request):
return render(request, 'page1.html')
Or, more conveniently to apply it to all uses of the cache_page
decorator you can set the default in your settings with:
FANCY_REMEMBER_ALL_URLS = True
Now, suppose you have the this option enabled. Now you can do things like this:
>>> from fancy_cache.memory import find_urls
>>> list(find_urls(['/some/searchpath', '/or/like/*/this.*']))
>>> # or, to get all:
>>> list(find_urls([]))
There is also another option to this and that is to purge (aka.
invalidate) the remembered URLs. You simply all the purge=True
option like this:
>>> from fancy_cache.memory import find_urls
>>> list(find_urls([], purge=True))
Note: Since find_urls()
returns a generator, the purging won't
happen unless you exhaust the generator. E.g. looping over it or
turning it into a list.
If you are using Memcached, you must enable check-and-set to
remember all urls by enabling the FANCY_USE_MEMCACHED_CHECK_AND_SET
flag and enabling cas
in your CACHES
settings:
# in settings.py
FANCY_USE_MEMCACHED_CHECK_AND_SET = True
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': '127.0.0.1:11211',
# This OPTIONS setting enables Memcached check-and-set which is
# required for remember_all_urls or FANCY_REMEMBER_ALL_URLS.
'OPTIONS': {
'behaviors': {
'cas': True
}
}
}
}
The second way to inspect all recorded URLs is to use the
fancy-cache
management command. This is only available if you have
added fancy_cache
to your INSTALLED_APPS
setting. Now you can do
this:
$ ./manage.py fancy-cache --help $ ./manage.py fancy-cache $ ./manage.py fancy-cache /some/searchpath /or/like/*/this.* $ ./manage.py fancy-cache /some/place/* --purge $ # or to purge them all! $ ./manage.py fancy-cache --purge
Note, it will only print out URLs that if found (and purged, if applicable).
The third way to inspect the recorded URLs is to add this to your root
urls.py
:
url(r'fancy-cache', include('fancy_cache.urls')),
Now, if you visit http://localhost:8000/fancy-cache
you get a table
listing every URL that django-fancy-cache
has recorded.
If you have enabled FANCY_REMEMBER_ALL_URLS
you can also enable
FANCY_REMEMBER_STATS_ALL_URLS
in your settings. What this does is
that it attempts to count the number of cache hits and cache misses
you have for each URL.
This counting of hits and misses is configured to last "a long time". Possibly longer than you cache your view. So, over time you can expect to have more than one miss because your view cache expires and it starts over.
You can see the stats whenever you use any of the ways described in the section above. For example like this:
>>> from fancy_cache.memory import find_urls
>>> found = list(find_urls([]))[0]
>>> found[0]
'/some/page.html'
>>> found[2]
{'hits': 1235, 'misses': 12}
There is obviously a small additional performance cost of using the
FANCY_REMEMBER_ALL_URLS
and/or FANCY_REMEMBER_STATS_ALL_URLS
in
your project so only use it if you don't have any smarter way to
invalidate, for debugging or if you really want make it possible to
purge all cached responses when you run an upgrade of your site or
something.
The simplest way is to simply run:
$ pip install tox $ tox
Or to run it without tox
you can simply run:
$ export PYTHONPATH=`pwd` $ export DJANGO_SETTINGS_MODULE=fancy_tests.tests.settings $ django-admin.py test
- 1.3.1
- Fix a bug whereby
FANCY_COMPRESS_REMEMBERED_URLS
setting raises a TypeError upon first implementation.
- Fix a bug whereby
- 1.3.0
- Enable
FANCY_COMPRESS_REMEMBERED_URLS
setting to compressremembered_urls
dictionary whenFANCY_REMEMBER_ALL_URLS
is True. - Bugfix: use correct location for
REMEMBERED_URLS
when using Memcached. - Add support for Python 3.11, Django 4.1 & 4.2
- Drop support for Python < 3.8, Django < 3.2, Django 4.0
- Enable
- 1.2.1
- Bugfix: conflict between the DummyCache backend when
FANCY_USE_MEMCACHED_CHECK_AND_SET
isTrue
- Bugfix: conflict between the DummyCache backend when
- 1.2.0
- Restructure the remembered_urls cache dict to clean up stale entries
- Update FancyCacheMiddleware to match latest Django CacheMiddlware (Also renames to FancyCacheMiddleware)
- Apply Memcached check-and-set to the delete_keys function
if
settings.FANCY_USE_MEMCACHED_CHECK_AND_SET = True
- Drop support for Python <3.6
- Add support for Python 3.10 and Django 4.0
- 1.1.0
- If you use Memcached you can set
settings.FANCY_USE_MEMCACHED_CHECK_AND_SET = True
so that you can usecache._cache.cas
which only workd with Memcached
- If you use Memcached you can set
- 1.0.0
- Drop support for Python <3.5 and Django <2.2.0
- 0.11.0
- Fix for
parse_qs
correctly between Python 2 and Python 3
- Fix for
- 0.10.0
- Fix for keeping blank strings in query strings. #39
- 0.9.0
- Django 1.10 support
- 0.8.2
- Remove deprecated way to define URL patterns and tests in python 3.5
- 0.8.1
- Ability to specify different cache backends to be used #31
- 0.8.0
- Started keeping a Changelog