I am having trouble deleting a @cache_page cache. I set it as @cache_page(60 * 5, key_prefix=‘my_prefix’) but this:
for key in cache._cache.keys():
if "my_prefix" in key:
print("my_prefix" in key)
print(cache.delete(key))
Returns True and False and the cache doesn’t get deleted. Is there really no simple way to delete a page cache? It seems aburd that even using the exact key doesn’t work.
What is the complete configuration for your CACHE? (Which cache backend are you using? How is it configured? What are all the CACHE-related settings in your project?)
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
}
How are your running your project?
Where does this code snippet reside? (Is this part of a view? If so, please post the complete view.)
Be aware that:
Note that each process will have its own private cache instance, which means no cross-process caching is possible. This also means the local memory cache isn’t particularly memory-efficient, so it’s probably not a good choice for production environments. It’s nice for development.
I’d be inclined to want to test this with a real cache backend.
It’s a development server and I was just testing this within a view. Let’s say I want to cache a page that contains a list of items and need to clear that cache when a new item is created/deleted, but I couldn’t find any way to do this, so I tried:
@cache_page(10, key_prefix='items_list')
def items_list(request):
items = Item.objects.all()
return render(request, context={'items':items}, template='items_list.html')
def test_view(request):
for key in cache._cache.keys():
print("items_list" in key)
print(cache.delete(key))
return redirect('items_list')
Returns:
True
False
But this should work regardless, no? The docs only mention using a key to delete the cache. And since @cache_page creates it’s own key, I don’t know what else to use as the key to delete the cache other than the exact key.
Where is this “test_view”, and how is it being called?
Again, I’m inclined to assume that what you’re seeing is caused by using the LocMemCache along with what you’re trying to do to test this. I’d suggest testing this with a real backend.
It’s in views and is called by requesting a page. Anyway, let’s say I want to use LocMemCache.
- What would be the recommended way to delete a page cache?
- Why does this not work, but deleting a cache by a key set manually does?
I tried caching this select widget (expensive to render in forms due to many options):
class MyWidget(forms.Select):
def render(self, name, value, attrs=None, renderer=None):
cache_key = f'my_widget'
cached_html = cache.get(cache_key)
if cached_html:
return cached_html
html = super().render(name, value, attrs, renderer)
cache.set(cache_key, html, 100)
return html
And deleting it with the set key works:
def test_view(request):
for key in cache._cache.keys():
if 'my_widget' in key:
print(key in cache._cache.keys())
print(key)
cache.delete('my_widget')
print(key in cache._cache.keys())
return redirect('items_list')
Output:
True
:1:my_widget
False
Clearly it does work with the key set manually, and again it doesn’t work when I try cache.delete(‘:1:my_widget’) instead of cache.delete(‘my_widget’).
Apparently cache.delete(key) takes the key without prefix, so cache.delete(key[3:]) does work. I thought I tried this before, but I used the wrong index. My bad.
Nevertheless, wouldn’t it be better if it was possible to set a custom key for page cache? Or at least being able to delete it with the key_prefix se tin the decorator?
Then be prepared for cache keys to randomly not exist, for views to not use cached results, and other potentially-unexpected behavior, in a production deployment.
I’m not following what you’re looking to do here.
Keep in mind that the cache_key in the decorator will be augmented by the url being used to make the request to the view.
From the docs for The per-view cache:
The per-view cache, like the per-site cache, is keyed off of the URL.
It would be interesting to print out all the keys in your test view to see exactly what’s being used.
I’m not following what you’re looking to do here.
As far as I understand:
- In order to delete a cache set by @cache_page decorator, I need to find the key to this cache programatically.
- I can do this, for example, by using the get_cache_key function and creating a fake request, or setting a key_prefix for @cache_page, iterating through all cache keys, finding the key by the prefix, stripping it off the version prefix set by django, and only then delete the cache:
@cache_page(3600, key_prefix='my_prefix')
def my_view(request):
...
def get_page_cache_keys(key_prefix):
keys = []
for key in cache._cache.keys():
if key_prefix in key:
keys.append(key[3:])
return keys
cache.delete_many(get_page_cache_keys('my_prefix')
Note that I have to strip the first three characters off the key (the version prefix given by django, e. g. “:1:”) in order to be able to delete it, because cache.delete(key) doesn’t work with the exact key.
This is, of course, not for production, just to point out my issue.
Thank you for the clarifications, that helps me a lot.
As far as my understanding goes (admittedly, not far), the key_prefix
should be at the beginning of every cache key generated by the cache_page decorator. (See default_key_func. I don’t see any cases within the Django-provided backends that override this behavior by default.)
Correct. The delete
method calls the make _and_validate_key
function to build the key with the prefix and version numbers added to it.
You could do this directly by calling the _delete
function which is what is called by delete
to delete the keys “pre-prefixed”. (Note that this would change if you switched to a more proper backend - and change differently for each.)
I do recommend reading the code for locmem.py
for understanding exactly what it’s doing. It should make things a lot clearer.
I want to add another thing that I think it’s related to this topic, that’s “Cache invalidation”. It’s really a pain to invalidate a cached page, with the current implementation. This is really common when you have a “list/detail” page, and a “update” object page. When you update the object you want to invalidate the cache (specially on a “detail” page).
Most of the times, what I end up doing is not using cache_page
at all, and using a manual cache get and when a manual invalidation when I update the object. I really wish that there was a better way of doing this kind of stuff. Because if you have control on how to invalidate a page, you can have more agressive caching for views that might end up doing a lot of queries/computation.
Thanks a lot, that’s clear now.