views:

90

answers:

3

Does Django caching have a method similar to Rails' cache.fetch? (http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html#M001023) The rails cache fetch works like:

cache.fetch("my_key") {
  // return what I want to put in my_key if it is empty
  "some_value"
}

It's useful because it checks the cache, and returns the value of the cache if it is there. If not, it will store "some_value" in the cache, and then return "some_value".

Is there an equivalent of this in Django? If not, what would the Python syntax for this look like if I were to implement such a function?

A: 

Well to get a default value if the key does not exits you can provide a second parameter:

cache.get('key', 'default')

cache.get() can take a default argument. This specifies which value to return if the object doesn't exist in the cache.

To save the default value in cache if the key does not exist, you can provide your custom cache backend. E.g. this extends the db cache backend (but works the same with others):

from django.core.cache.backends import db

class CustomCache(db.CacheClass):

    def get(self, key, default=None):
        result = super(CustomCache, self).get(key, default)

        if result == default:
            self.add(key, default)
            return default

        return result

But I don't think that this adds any value.

Update:
In response to the comment on the other post: Yes it compares the default value with the returned value and if both are equal, the value gets added to the cache. But cache.add only sets the new value if the key is not already in the cache (contrast to cache.set which always overrides):

To add a key only if it doesn't already exist, use the add() method. It takes the same parameters as set(), but it will not attempt to update the cache if the key specified is already present.

Felix Kling
See my comment on Julian's answer. This does not add value because it forces you to always execute code to get *value* _before_ you send it to cache.get
Idris
@Idris: For me, it sounds that this `fetch` command is the same as `get` in Django. I understand that in addition, `fetch` writes the value back in the cache but whenever you call `fetch(key)` you have to provide a block for the default value as you don't know whether a default value was written to the cache before or not. Can you explain your point better? Also this *execute code to get value before you send...*: Isn't `fetch` the same? You execute `fetch(key)` to get the value of `key` and if it is not in cache, it gets inserted....
Felix Kling
+3  A: 

I think the code you would have to write would be like this: (EDIT)

def get_value(param1,param2):
    return "value %s - %s " % (str(param1),str(param2))

def fetch(key,val_function,**kwargs)
    val = cache.get(key)
    if not val:
        val = val_function(**kwargs)
        cache.set(key,val)
    return val

and you would call it like this:

fetch('key',get_value,param1='first',param2='second')
Julian
This, as well as Felix's answer below, has the problem that it evaluates _value_ even if the cache already contains a value. That defeats the point of caching. Normally, the block you pass in to Rails' cache.fetch would do some database lookups, etc. So, any Django fetch method would have to use closures of some sort.
Idris
I've edited the code... this should do it
Julian
This actually works, passing val_function to the fetch method. However, this gets a bit more tricky when you need to pass parameters. I guess this is a strong point for Ruby's block syntax. Anyway, thanks for the help Julian!
Idris
I've once more updated the code so you can pass parameters... hope it helps :)
Julian
+2  A: 

Julian's code is quite good but doesn't take positional args (when you want to use sorted() for example). Here's my fix:

def get_value(param1,param2):
    return "value %s - %s " % (str(param1),str(param2))

def fetch(key,val_function, *args, **kwargs)
    val = cache.get(key)
    if not val:
        val = val_function(*args, **kwargs)
        cache.set(key,val)
    return val
grucha