Index: django/core/cache/backends/simple.py
===================================================================
--- django/core/cache/backends/simple.py	(revision 5648)
+++ django/core/cache/backends/simple.py	(working copy)
@@ -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)
Index: django/core/cache/backends/base.py
===================================================================
--- django/core/cache/backends/base.py	(revision 5648)
+++ django/core/cache/backends/base.py	(working copy)
@@ -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
Index: django/core/cache/backends/dummy.py
===================================================================
--- django/core/cache/backends/dummy.py	(revision 5648)
+++ django/core/cache/backends/dummy.py	(working copy)
@@ -6,6 +6,9 @@
     def __init__(self, *args, **kwargs):
         pass
 
+    def add(self, *args, **kwargs):
+        pass
+
     def get(self, key, default=None):
         return default
 
Index: django/core/cache/backends/locmem.py
===================================================================
--- django/core/cache/backends/locmem.py	(revision 5648)
+++ django/core/cache/backends/locmem.py	(working copy)
@@ -9,6 +9,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()
Index: django/core/cache/backends/filebased.py
===================================================================
--- django/core/cache/backends/filebased.py	(revision 5648)
+++ django/core/cache/backends/filebased.py	(working copy)
@@ -16,6 +16,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:
Index: django/core/cache/backends/db.py
===================================================================
--- django/core/cache/backends/db.py	(revision 5648)
+++ django/core/cache/backends/db.py	(working copy)
@@ -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,19 @@
         return pickle.loads(base64.decodestring(row[1]))
 
     def set(self, key, value, timeout=None):
+        return self._base_set('set', key, value, timeout)
+
+    def delete(self, key):
+        cursor = connection.cursor()
+        cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % self._table, [key])
+        transaction.commit_unless_managed()
+
+    def has_key(self, key):
+        cursor = connection.cursor()
+        cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s" % self._table, [key])
+        return cursor.fetchone() is not None
+
+    def _base_set(self, mode, key, value, timeout=None):
         if timeout is None:
             timeout = self.default_timeout
         cursor = connection.cursor()
@@ -50,26 +66,17 @@
         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
         else:
             transaction.commit_unless_managed()
 
-    def delete(self, key):
-        cursor = connection.cursor()
-        cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % self._table, [key])
-        transaction.commit_unless_managed()
-
-    def has_key(self, key):
-        cursor = connection.cursor()
-        cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s" % self._table, [key])
-        return cursor.fetchone() is not None
-
     def _cull(self, cursor, now):
         if self._cull_frequency == 0:
             cursor.execute("DELETE FROM %s" % self._table)
Index: django/core/cache/backends/memcached.py
===================================================================
--- django/core/cache/backends/memcached.py	(revision 5648)
+++ django/core/cache/backends/memcached.py	(working copy)
@@ -15,6 +15,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(key)
         if val is None:
Index: tests/regressiontests/cache/tests.py
===================================================================
--- tests/regressiontests/cache/tests.py	(revision 5648)
+++ tests/regressiontests/cache/tests.py	(working copy)
@@ -17,6 +17,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)
Index: docs/cache.txt
===================================================================
--- docs/cache.txt	(revision 5648)
+++ docs/cache.txt	(working copy)
@@ -324,6 +324,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)::
