Author: mtredinnick
Date: 2007-10-20 10:16:34 -0500 (Sat, 20 Oct 2007)
New Revision: 6572

Modified:
   django/trunk/django/core/cache/backends/base.py
   django/trunk/django/core/cache/backends/db.py
   django/trunk/django/core/cache/backends/dummy.py
   django/trunk/django/core/cache/backends/filebased.py
   django/trunk/django/core/cache/backends/locmem.py
   django/trunk/django/core/cache/backends/memcached.py
   django/trunk/django/core/cache/backends/simple.py
   django/trunk/docs/cache.txt
   django/trunk/tests/regressiontests/cache/tests.py
Log:
Fixed #4831 -- Added an "add" cache key method, for parity with memcached's
API. This works for all cache backends. Patch from Matt McClanahan.


Modified: django/trunk/django/core/cache/backends/base.py
===================================================================
--- django/trunk/django/core/cache/backends/base.py     2007-10-20 15:01:31 UTC 
(rev 6571)
+++ django/trunk/django/core/cache/backends/base.py     2007-10-20 15:16:34 UTC 
(rev 6572)
@@ -14,6 +14,14 @@
             timeout = 300
         self.default_timeout = timeout
 
+    def add(self, key, value, timeout=None):
+        """
+        Set a value in the cache if the key does not already exist.  If
+        timeout is given, that timeout will be used for the key; otherwise
+        the default cache timeout will be used.
+        """
+        raise NotImplementedError
+
     def get(self, key, default=None):
         """
         Fetch a given key from the cache.  If the key does not exist, return

Modified: django/trunk/django/core/cache/backends/db.py
===================================================================
--- django/trunk/django/core/cache/backends/db.py       2007-10-20 15:01:31 UTC 
(rev 6571)
+++ django/trunk/django/core/cache/backends/db.py       2007-10-20 15:16:34 UTC 
(rev 6572)
@@ -24,6 +24,9 @@
         except (ValueError, TypeError):
             self._cull_frequency = 3
 
+    def add(self, key, value, timeout=None):
+        return self._base_set('add', key, value, timeout)
+
     def get(self, key, default=None):
         cursor = connection.cursor()
         cursor.execute("SELECT cache_key, value, expires FROM %s WHERE 
cache_key = %%s" % self._table, [key])
@@ -38,6 +41,9 @@
         return pickle.loads(base64.decodestring(row[1]))
 
     def set(self, key, value, timeout=None):
+        return self._base_set('set', key, value, timeout)
+
+    def _base_set(self, mode, key, value, timeout=None):
         if timeout is None:
             timeout = self.default_timeout
         cursor = connection.cursor()
@@ -50,10 +56,11 @@
         encoded = base64.encodestring(pickle.dumps(value, 2)).strip()
         cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s" % 
self._table, [key])
         try:
-            if cursor.fetchone():
+            if mode == 'set' and cursor.fetchone():
                 cursor.execute("UPDATE %s SET value = %%s, expires = %%s WHERE 
cache_key = %%s" % self._table, [encoded, str(exp), key])
             else:
-                cursor.execute("INSERT INTO %s (cache_key, value, expires) 
VALUES (%%s, %%s, %%s)" % self._table, [key, encoded, str(exp)])
+                if mode == 'add':
+                    cursor.execute("INSERT INTO %s (cache_key, value, expires) 
VALUES (%%s, %%s, %%s)" % self._table, [key, encoded, str(exp)])
         except DatabaseError:
             # To be threadsafe, updates/inserts are allowed to fail silently
             pass

Modified: django/trunk/django/core/cache/backends/dummy.py
===================================================================
--- django/trunk/django/core/cache/backends/dummy.py    2007-10-20 15:01:31 UTC 
(rev 6571)
+++ django/trunk/django/core/cache/backends/dummy.py    2007-10-20 15:16:34 UTC 
(rev 6572)
@@ -6,6 +6,9 @@
     def __init__(self, *args, **kwargs):
         pass
 
+    def add(self, *args, **kwargs):
+        pass
+
     def get(self, key, default=None):
         return default
 

Modified: django/trunk/django/core/cache/backends/filebased.py
===================================================================
--- django/trunk/django/core/cache/backends/filebased.py        2007-10-20 
15:01:31 UTC (rev 6571)
+++ django/trunk/django/core/cache/backends/filebased.py        2007-10-20 
15:16:34 UTC (rev 6572)
@@ -17,6 +17,26 @@
         del self._cache
         del self._expire_info
 
+    def add(self, key, value, timeout=None):
+        fname = self._key_to_file(key)
+        if timeout is None:
+            timeout = self.default_timeout
+        try:
+            filelist = os.listdir(self._dir)
+        except (IOError, OSError):
+            self._createdir()
+            filelist = []
+        if len(filelist) > self._max_entries:
+            self._cull(filelist)
+        if os.path.basename(fname) not in filelist:
+            try:
+                f = open(fname, 'wb')
+                now = time.time()
+                pickle.dump(now + timeout, f, 2)
+                pickle.dump(value, f, 2)
+            except (IOError, OSError):
+                pass
+
     def get(self, key, default=None):
         fname = self._key_to_file(key)
         try:

Modified: django/trunk/django/core/cache/backends/locmem.py
===================================================================
--- django/trunk/django/core/cache/backends/locmem.py   2007-10-20 15:01:31 UTC 
(rev 6571)
+++ django/trunk/django/core/cache/backends/locmem.py   2007-10-20 15:16:34 UTC 
(rev 6572)
@@ -14,6 +14,13 @@
         SimpleCacheClass.__init__(self, host, params)
         self._lock = RWLock()
 
+    def add(self, key, value, timeout=None):
+        self._lock.writer_enters()
+        try:
+            SimpleCacheClass.add(self, key, value, timeout)
+        finally:
+            self._lock.writer_leaves()
+
     def get(self, key, default=None):
         should_delete = False
         self._lock.reader_enters()

Modified: django/trunk/django/core/cache/backends/memcached.py
===================================================================
--- django/trunk/django/core/cache/backends/memcached.py        2007-10-20 
15:01:31 UTC (rev 6571)
+++ django/trunk/django/core/cache/backends/memcached.py        2007-10-20 
15:16:34 UTC (rev 6572)
@@ -16,6 +16,9 @@
         BaseCache.__init__(self, params)
         self._cache = memcache.Client(server.split(';'))
 
+    def add(self, key, value, timeout=0):
+        self._cache.add(key.encode('ascii', 'ignore'), value, timeout or 
self.default_timeout)
+
     def get(self, key, default=None):
         val = self._cache.get(smart_str(key))
         if val is None:

Modified: django/trunk/django/core/cache/backends/simple.py
===================================================================
--- django/trunk/django/core/cache/backends/simple.py   2007-10-20 15:01:31 UTC 
(rev 6571)
+++ django/trunk/django/core/cache/backends/simple.py   2007-10-20 15:16:34 UTC 
(rev 6572)
@@ -21,6 +21,15 @@
         except (ValueError, TypeError):
             self._cull_frequency = 3
 
+    def add(self, key, value, timeout=None):
+        if len(self._cache) >= self._max_entries:
+            self._cull()
+        if timeout is None:
+            timeout = self.default_timeout
+        if key not in self._cache.keys():
+            self._cache[key] = value
+            self._expire_info[key] = time.time() + timeout
+
     def get(self, key, default=None):
         now = time.time()
         exp = self._expire_info.get(key)

Modified: django/trunk/docs/cache.txt
===================================================================
--- django/trunk/docs/cache.txt 2007-10-20 15:01:31 UTC (rev 6571)
+++ django/trunk/docs/cache.txt 2007-10-20 15:16:34 UTC (rev 6572)
@@ -326,6 +326,15 @@
     >>> cache.get('my_key', 'has expired')
     'has expired'
 
+To add a key only if it doesn't already exist, there is an add() method.  It
+takes the same parameters as set(), but will not attempt to update the cache
+if the key specified is already present::
+
+    >>> cache.set('add_key', 'Initial value')
+    >>> cache.add('add_key', 'New value')
+    >>> cache.get('add_key')
+    'Initial value'
+
 There's also a get_many() interface that only hits the cache once. get_many()
 returns a dictionary with all the keys you asked for that actually exist in the
 cache (and haven't expired)::

Modified: django/trunk/tests/regressiontests/cache/tests.py
===================================================================
--- django/trunk/tests/regressiontests/cache/tests.py   2007-10-20 15:01:31 UTC 
(rev 6571)
+++ django/trunk/tests/regressiontests/cache/tests.py   2007-10-20 15:16:34 UTC 
(rev 6572)
@@ -19,6 +19,12 @@
         cache.set("key", "value")
         self.assertEqual(cache.get("key"), "value")
 
+    def test_add(self):
+        # test add (only add if key isn't already in cache)
+        cache.add("addkey1", "value")
+        cache.add("addkey1", "newvalue")
+        self.assertEqual(cache.get("addkey1"), "value")
+
     def test_non_existent(self):
         # get with non-existent keys
         self.assertEqual(cache.get("does_not_exist"), None)


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to