chibisov / drf-extensions

DRF-extensions is a collection of custom extensions for Django REST Framework
http://chibisov.github.io/drf-extensions/docs
MIT License
1.47k stars 208 forks source link

Invalidating cache? #151

Closed ckcollab closed 8 years ago

ckcollab commented 8 years ago

First of all: thanks so much for all of the hard work on this! I was able to implement caching super fast.

It'd be awesome if I could somehow invalidate all of the cache keys for a given view, for example if I have caching for "Companies" in my app and I update Companies, it'd be great if I could just say: UserKeyConstructor.invalidate_all() or some such.

Is this possible? I noticed from key construction they are hashed, so we couldn't do some kind of key removal based on a string/prefix...

Thanks again and have a good one!

chibisov commented 8 years ago

Hi. You can try to implement custom key bit

ckcollab commented 8 years ago

Hrm, how would I invalidate many cache keys at once, though, if all the keys are hashed eventually? For example, say I have a view with query params like ?office=1 or ?office=2 -- what if I want to iterate over all keys to remove them? Or maybe if I make a custom key, it's not hashed??? Maybe then I could use a prefix and remove all keys with some prefix?

Thanks for the quick advice! Really appreciate it!

ckcollab commented 8 years ago

Actually think I figured something out, I can do something like this:

class PrefixedKeyConstructor(KeyConstructor):

    def __init__(self, unique_key_name, *args, **kwargs):
        self.unique_key_name = unique_key_name
        super().__init__(*args, **kwargs)

    def prepare_key(self, key_dict):
        key = super().prepare_key(key_dict)
        return '{}{}'.format(self.unique_key_name, key)

class UserKeyConstructor(PrefixedKeyConstructor):
    unique_method_id = bits.UniqueMethodIdKeyBit()
    user = bits.UserKeyBit()
    query_params = bits.QueryParamsKeyBit(['*'])

And invalidate all the keys like so:

from django.core.cache import cache
cache.delete_pattern("foo_*")

and fire this off with signals:

def invalidate_offices(sender=None, instance=None, *args, **kwargs):
    cache.delete_pattern('*api:office*')

post_save.connect(receiver=invalidate_offices, sender=Office)
post_delete.connect(receiver=invalidate_offices, sender=Office)
ckcollab commented 8 years ago

Yep that worked beautifully, thanks so much!

benrevl commented 7 years ago

The solution by @ckcollab here is better than the official documentations solution -- this actually removes the cache entries instead of adding new ones. The solution in the official documentation will eventually fill the cache with redundant previously cached data.

auvipy commented 6 years ago

could anyone care to send a pr with improved example?

LukGerman commented 6 years ago

Thanks for nice idea, @ckcollab which cache backend are you using? In my project I tried PyLibMCCache, but it doesn't support delete_pattern. I'm thinking of switching to django_redis.

ckcollab commented 6 years ago

"BACKEND": "django_redis.cache.RedisCache", apparently :)

Also, I was using LocMem in testing successfully, I believe?