Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #4831 -- Added an "add" cache key method, for parity with memca…

…ched's

API. This works for all cache backends. Patch from Matt McClanahan.


git-svn-id: http://code.djangoproject.com/svn/django/trunk@6572 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 714c09b8bdba069d2de49c5cf58f500497711f85 1 parent b678601
Malcolm Tredinnick authored October 20, 2007
8  django/core/cache/backends/base.py
@@ -14,6 +14,14 @@ def __init__(self, params):
14 14
             timeout = 300
15 15
         self.default_timeout = timeout
16 16
 
  17
+    def add(self, key, value, timeout=None):
  18
+        """
  19
+        Set a value in the cache if the key does not already exist.  If
  20
+        timeout is given, that timeout will be used for the key; otherwise
  21
+        the default cache timeout will be used.
  22
+        """
  23
+        raise NotImplementedError
  24
+
17 25
     def get(self, key, default=None):
18 26
         """
19 27
         Fetch a given key from the cache.  If the key does not exist, return
11  django/core/cache/backends/db.py
@@ -24,6 +24,9 @@ def __init__(self, table, params):
24 24
         except (ValueError, TypeError):
25 25
             self._cull_frequency = 3
26 26
 
  27
+    def add(self, key, value, timeout=None):
  28
+        return self._base_set('add', key, value, timeout)
  29
+
27 30
     def get(self, key, default=None):
28 31
         cursor = connection.cursor()
29 32
         cursor.execute("SELECT cache_key, value, expires FROM %s WHERE cache_key = %%s" % self._table, [key])
@@ -38,6 +41,9 @@ def get(self, key, default=None):
38 41
         return pickle.loads(base64.decodestring(row[1]))
39 42
 
40 43
     def set(self, key, value, timeout=None):
  44
+        return self._base_set('set', key, value, timeout)
  45
+
  46
+    def _base_set(self, mode, key, value, timeout=None):
41 47
         if timeout is None:
42 48
             timeout = self.default_timeout
43 49
         cursor = connection.cursor()
@@ -50,10 +56,11 @@ def set(self, key, value, timeout=None):
50 56
         encoded = base64.encodestring(pickle.dumps(value, 2)).strip()
51 57
         cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s" % self._table, [key])
52 58
         try:
53  
-            if cursor.fetchone():
  59
+            if mode == 'set' and cursor.fetchone():
54 60
                 cursor.execute("UPDATE %s SET value = %%s, expires = %%s WHERE cache_key = %%s" % self._table, [encoded, str(exp), key])
55 61
             else:
56  
-                cursor.execute("INSERT INTO %s (cache_key, value, expires) VALUES (%%s, %%s, %%s)" % self._table, [key, encoded, str(exp)])
  62
+                if mode == 'add':
  63
+                    cursor.execute("INSERT INTO %s (cache_key, value, expires) VALUES (%%s, %%s, %%s)" % self._table, [key, encoded, str(exp)])
57 64
         except DatabaseError:
58 65
             # To be threadsafe, updates/inserts are allowed to fail silently
59 66
             pass
3  django/core/cache/backends/dummy.py
@@ -6,6 +6,9 @@ class CacheClass(BaseCache):
6 6
     def __init__(self, *args, **kwargs):
7 7
         pass
8 8
 
  9
+    def add(self, *args, **kwargs):
  10
+        pass
  11
+
9 12
     def get(self, key, default=None):
10 13
         return default
11 14
 
20  django/core/cache/backends/filebased.py
@@ -17,6 +17,26 @@ def __init__(self, dir, params):
17 17
         del self._cache
18 18
         del self._expire_info
19 19
 
  20
+    def add(self, key, value, timeout=None):
  21
+        fname = self._key_to_file(key)
  22
+        if timeout is None:
  23
+            timeout = self.default_timeout
  24
+        try:
  25
+            filelist = os.listdir(self._dir)
  26
+        except (IOError, OSError):
  27
+            self._createdir()
  28
+            filelist = []
  29
+        if len(filelist) > self._max_entries:
  30
+            self._cull(filelist)
  31
+        if os.path.basename(fname) not in filelist:
  32
+            try:
  33
+                f = open(fname, 'wb')
  34
+                now = time.time()
  35
+                pickle.dump(now + timeout, f, 2)
  36
+                pickle.dump(value, f, 2)
  37
+            except (IOError, OSError):
  38
+                pass
  39
+
20 40
     def get(self, key, default=None):
21 41
         fname = self._key_to_file(key)
22 42
         try:
7  django/core/cache/backends/locmem.py
@@ -14,6 +14,13 @@ def __init__(self, host, params):
14 14
         SimpleCacheClass.__init__(self, host, params)
15 15
         self._lock = RWLock()
16 16
 
  17
+    def add(self, key, value, timeout=None):
  18
+        self._lock.writer_enters()
  19
+        try:
  20
+            SimpleCacheClass.add(self, key, value, timeout)
  21
+        finally:
  22
+            self._lock.writer_leaves()
  23
+
17 24
     def get(self, key, default=None):
18 25
         should_delete = False
19 26
         self._lock.reader_enters()
3  django/core/cache/backends/memcached.py
@@ -16,6 +16,9 @@ def __init__(self, server, params):
16 16
         BaseCache.__init__(self, params)
17 17
         self._cache = memcache.Client(server.split(';'))
18 18
 
  19
+    def add(self, key, value, timeout=0):
  20
+        self._cache.add(key.encode('ascii', 'ignore'), value, timeout or self.default_timeout)
  21
+
19 22
     def get(self, key, default=None):
20 23
         val = self._cache.get(smart_str(key))
21 24
         if val is None:
9  django/core/cache/backends/simple.py
@@ -21,6 +21,15 @@ def __init__(self, host, params):
21 21
         except (ValueError, TypeError):
22 22
             self._cull_frequency = 3
23 23
 
  24
+    def add(self, key, value, timeout=None):
  25
+        if len(self._cache) >= self._max_entries:
  26
+            self._cull()
  27
+        if timeout is None:
  28
+            timeout = self.default_timeout
  29
+        if key not in self._cache.keys():
  30
+            self._cache[key] = value
  31
+            self._expire_info[key] = time.time() + timeout
  32
+
24 33
     def get(self, key, default=None):
25 34
         now = time.time()
26 35
         exp = self._expire_info.get(key)
9  docs/cache.txt
@@ -326,6 +326,15 @@ get() can take a ``default`` argument::
326 326
     >>> cache.get('my_key', 'has expired')
327 327
     'has expired'
328 328
 
  329
+To add a key only if it doesn't already exist, there is an add() method.  It
  330
+takes the same parameters as set(), but will not attempt to update the cache
  331
+if the key specified is already present::
  332
+
  333
+    >>> cache.set('add_key', 'Initial value')
  334
+    >>> cache.add('add_key', 'New value')
  335
+    >>> cache.get('add_key')
  336
+    'Initial value'
  337
+
329 338
 There's also a get_many() interface that only hits the cache once. get_many()
330 339
 returns a dictionary with all the keys you asked for that actually exist in the
331 340
 cache (and haven't expired)::
6  tests/regressiontests/cache/tests.py
@@ -19,6 +19,12 @@ def test_simple(self):
19 19
         cache.set("key", "value")
20 20
         self.assertEqual(cache.get("key"), "value")
21 21
 
  22
+    def test_add(self):
  23
+        # test add (only add if key isn't already in cache)
  24
+        cache.add("addkey1", "value")
  25
+        cache.add("addkey1", "newvalue")
  26
+        self.assertEqual(cache.get("addkey1"), "value")
  27
+
22 28
     def test_non_existent(self):
23 29
         # get with non-existent keys
24 30
         self.assertEqual(cache.get("does_not_exist"), None)

0 notes on commit 714c09b

Please sign in to comment.
Something went wrong with that request. Please try again.