Index: django/db/models/sql/query.py
===================================================================
--- django/db/models/sql/query.py	(revision 8961)
+++ django/db/models/sql/query.py	(working copy)
@@ -71,6 +71,8 @@
         self.order_by = []
         self.low_mark, self.high_mark = 0, None  # Used for offset/limit
         self.distinct = False
+        self.select_for_update = False
+        self.select_for_update_nowait = False
         self.select_related = False
         self.related_select_cols = []
 
@@ -179,6 +181,8 @@
         obj.order_by = self.order_by[:]
         obj.low_mark, obj.high_mark = self.low_mark, self.high_mark
         obj.distinct = self.distinct
+        obj.select_for_update = self.select_for_update
+        obj.select_for_update_nowait = self.select_for_update_nowait
         obj.select_related = self.select_related
         obj.related_select_cols = []
         obj.max_depth = self.max_depth
@@ -225,6 +229,7 @@
         obj = self.clone()
         obj.clear_ordering(True)
         obj.clear_limits()
+        obj.select_for_update = False
         obj.select_related = False
         obj.related_select_cols = []
         obj.related_select_fields = []
@@ -305,6 +310,9 @@
                         result.append('LIMIT %d' % val)
                 result.append('OFFSET %d' % self.low_mark)
 
+        if self.select_for_update:
+            result.append("%s" % self.connection.ops.for_update_sql(nowait=self.select_for_update_nowait))
+
         params.extend(self.extra_params)
         return ' '.join(result), tuple(params)
 
Index: django/db/models/manager.py
===================================================================
--- django/db/models/manager.py	(revision 8961)
+++ django/db/models/manager.py	(working copy)
@@ -119,6 +119,9 @@
     def order_by(self, *args, **kwargs):
         return self.get_query_set().order_by(*args, **kwargs)
 
+    def select_for_update(self, *args, **kwargs):
+        return self.get_query_set().select_for_update(*args, **kwargs)
+        
     def select_related(self, *args, **kwargs):
         return self.get_query_set().select_related(*args, **kwargs)
 
Index: django/db/models/query.py
===================================================================
--- django/db/models/query.py	(revision 8961)
+++ django/db/models/query.py	(working copy)
@@ -379,6 +379,7 @@
         del_query = self._clone()
 
         # Disable non-supported fields.
+        del_query.query.select_for_update = False
         del_query.query.select_related = False
         del_query.query.clear_ordering()
 
@@ -518,6 +519,18 @@
         else:
             return self._filter_or_exclude(None, **filter_obj)
 
+    def select_for_update(self, **kwargs):
+        """
+        Returns a new QuerySet instance that will select objects with a
+        FOR UPDATE lock.
+        """
+        # Default to false for nowait
+        nowait = kwargs.pop('nowait', False)
+        obj = self._clone()
+        obj.query.select_for_update = True
+        obj.query.select_for_update_nowait = nowait
+        return obj
+
     def select_related(self, *fields, **kwargs):
         """
         Returns a new QuerySet instance that will select related objects.
Index: django/db/backends/sqlite3/base.py
===================================================================
--- django/db/backends/sqlite3/base.py	(revision 8961)
+++ django/db/backends/sqlite3/base.py	(working copy)
@@ -67,6 +67,10 @@
         # function django_date_trunc that's registered in connect().
         return 'django_date_trunc("%s", %s)' % (lookup_type.lower(), field_name)
 
+    def for_update_sql(self, nowait=False):
+        # sqlite does not support FOR UPDATE
+        return ''
+
     def drop_foreignkey_sql(self):
         return ""
 
@@ -202,3 +206,4 @@
         return bool(re.search(re_pattern, re_string))
     except:
         return False
+
Index: django/db/backends/mysql/base.py
===================================================================
--- django/db/backends/mysql/base.py	(revision 8961)
+++ django/db/backends/mysql/base.py	(working copy)
@@ -136,6 +136,16 @@
     def fulltext_search_sql(self, field_name):
         return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
 
+    def for_update_sql(self, nowait=False):
+        """
+        Return FOR UPDATE SQL clause to lock row for update
+        Currently mysql ignores NOWAIT
+        """
+        if nowait:
+            raise NotImplementedError("NOWAIT option for SELECT ... FOR UPDATE not implemented with mysql");
+        return super(DatabaseOperations, self).for_update_sql(nowait=False)
+
+
     def no_limit_value(self):
         # 2**64 - 1, as recommended by the MySQL documentation
         return 18446744073709551615L
Index: django/db/backends/__init__.py
===================================================================
--- django/db/backends/__init__.py	(revision 8961)
+++ django/db/backends/__init__.py	(working copy)
@@ -143,6 +143,16 @@
         """
         return '%s'
 
+    def for_update_sql(self, nowait=False):
+        """
+        Return FOR UPDATE SQL clause to lock row for update
+        """
+        if nowait:
+            nowaitstr = ' NOWAIT'
+        else:
+            nowaitstr = ''
+        return 'FOR UPDATE' + nowaitstr
+
     def fulltext_search_sql(self, field_name):
         """
         Returns the SQL WHERE clause to use in order to perform a full-text
Index: tests/regressiontests/queries/models.py
===================================================================
--- tests/regressiontests/queries/models.py	(revision 8961)
+++ tests/regressiontests/queries/models.py	(working copy)
@@ -227,6 +227,17 @@
     def __unicode__(self):
         return self.name
 
+from django.db import transaction
+def test_for_update():
+    t = Tag(name='forupdate')
+    t.save()
+    transaction.commit()
+    tfound = Tag.objects.select_for_update().get(pk=t.id)
+    tfound.name = 'forupdate2'
+    tfound.save()
+    transaction.commit()
+test_for_update = transaction.commit_manually(test_for_update)
+
 __test__ = {'API_TESTS':"""
 >>> t1 = Tag.objects.create(name='t1')
 >>> t2 = Tag.objects.create(name='t2', parent=t1)
@@ -953,6 +964,10 @@
 >>> len([x[2] for x in q.alias_map.values() if x[2] == q.LOUTER and q.alias_refcount[x[1]]])
 1
 
+Bug #2075
+Added FOR UPDATE functionality
+>>> test_for_update()
+>>> Tag.objects.get(name='forupdate2').delete()
 """}
 
 # In Python 2.3 and the Python 2.6 beta releases, exceptions raised in __len__
@@ -986,3 +1001,4 @@
 []
 
 """
+
Index: docs/ref/models/querysets.txt
===================================================================
--- docs/ref/models/querysets.txt	(revision 8961)
+++ docs/ref/models/querysets.txt	(working copy)
@@ -627,6 +627,36 @@
 
         Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
 
+select_for_update
+~~~~~~~~~~~~~~~~~
+
+**New in Django development version:**
+
+Lock rows returned from a query. Most databases allow you to exclusively
+lock rows with ``select_for_update()``. To do this call the method
+``select_for_update()`` to lock all retrieved objects::
+
+    entry = Entry.objects.select_for_update().get(pk=1)
+    ...
+    entry.save()
+
+All objects returned using ``select_for_update()`` will be locked with an
+exclusive lock which will remain locked until the transaction has finished.
+In the case of using middleware, the locks will be released when the view
+returns and the transaction is committed or rolled back. SQLite does not
+support an exclusive lock so this is simply ignored for SQLite. Other
+databases issue a ``FOR UPDATE``.
+
+If you would not like to wait for the lock then set the parameter nowait to
+True. In this case, it will grab the lock if it isn't already locked. If
+it is locked then it will throw an exception. This is not supported
+with mysql. Doing this with mysql will raise a ``NotImplemented`` exception.
+
+Note that all rows returned are locked.  If you retrieve multiple objects,
+all objects will be locked until the transaction is committed. Another
+process which does a ``select_for_update()`` on the same rows will wait until
+the transaction which has the locks is finished.
+
 QuerySet methods that do not return QuerySets
 ---------------------------------------------
 
