diff --git a/django/contrib/gis/db/backend/adaptor.py b/django/contrib/gis/db/backend/adaptor.py
index b2397e6..7659aef 100644
--- a/django/contrib/gis/db/backend/adaptor.py
+++ b/django/contrib/gis/db/backend/adaptor.py
@@ -8,7 +8,10 @@ class WKTAdaptor(object):
         self.srid = geom.srid
 
     def __eq__(self, other):
-        return self.wkt == other.wkt and self.srid == other.srid 
+        return self.wkt == other.wkt and self.srid == other.srid
 
     def __str__(self):
         return self.wkt
+
+    def prepare_database_save(self, unused):
+        return self
diff --git a/django/contrib/gis/db/backend/postgis/adaptor.py b/django/contrib/gis/db/backend/postgis/adaptor.py
index c094a98..7deada4 100644
--- a/django/contrib/gis/db/backend/postgis/adaptor.py
+++ b/django/contrib/gis/db/backend/postgis/adaptor.py
@@ -31,3 +31,6 @@ class PostGISAdaptor(object):
         "Returns a properly quoted string for use in PostgreSQL/PostGIS."
         # Want to use WKB, so wrap with psycopg2 Binary() to quote properly.
         return "%s(%s, %s)" % (GEOM_FROM_WKB, Binary(self.wkb), self.srid or -1)
+
+    def prepare_database_save(self, unused):
+        return self
diff --git a/django/contrib/gis/tests/geoapp/test_regress.py b/django/contrib/gis/tests/geoapp/test_regress.py
new file mode 100644
index 0000000..02a0f09
--- /dev/null
+++ b/django/contrib/gis/tests/geoapp/test_regress.py
@@ -0,0 +1,18 @@
+import os, unittest
+from django.test import TestCase
+from django.contrib.gis.db.backend import SpatialBackend
+from django.contrib.gis.tests.utils import no_mysql, no_oracle, no_postgis
+from models import City
+
+class GeoRegressionTests(TestCase):
+    def test01_update(self):
+        "Testing GeoQuerySet.update(). See #10411."
+        pnt = City.objects.get(name='Pueblo').point
+        bak = pnt.clone()
+        pnt.y += 0.005
+
+        City.objects.filter(name='Pueblo').update(point=pnt)
+        self.assertEqual(pnt, City.objects.get(name='Pueblo').point)
+        City.objects.filter(name='Pueblo').update(point=bak)
+        self.assertEqual(bak, City.objects.get(name='Pueblo').point)
+
diff --git a/django/contrib/gis/tests/geoapp/tests.py b/django/contrib/gis/tests/geoapp/tests.py
index fa93cbc..f5128cf 100644
--- a/django/contrib/gis/tests/geoapp/tests.py
+++ b/django/contrib/gis/tests/geoapp/tests.py
@@ -13,7 +13,7 @@ from django.contrib.gis.tests.utils import no_oracle, no_postgis
 DISABLE = False
 
 class GeoModelTest(unittest.TestCase):
-    
+
     def test01_initial_sql(self):
         "Testing geographic initial SQL."
         if DISABLE: return
@@ -21,7 +21,7 @@ class GeoModelTest(unittest.TestCase):
             # Oracle doesn't allow strings longer than 4000 characters
             # in SQL files, and I'm stumped on how to use Oracle BFILE's
             # in PLSQL, so we set up the larger geometries manually, rather
-            # than relying on the initial SQL. 
+            # than relying on the initial SQL.
 
             # Routine for returning the path to the data files.
             data_dir = os.path.join(os.path.dirname(__file__), 'sql')
@@ -65,7 +65,7 @@ class GeoModelTest(unittest.TestCase):
         new = Point(5, 23)
         nullcity.point = new
 
-        # Ensuring that the SRID is automatically set to that of the 
+        # Ensuring that the SRID is automatically set to that of the
         #  field after assignment, but before saving.
         self.assertEqual(4326, nullcity.point.srid)
         nullcity.save()
@@ -94,7 +94,7 @@ class GeoModelTest(unittest.TestCase):
 
         ns = State.objects.get(name='NullState')
         self.assertEqual(ply, ns.poly)
-        
+
         # Testing the `ogr` and `srs` lazy-geometry properties.
         if gdal.HAS_GDAL:
             self.assertEqual(True, isinstance(ns.poly.ogr, gdal.OGRGeometry))
@@ -120,7 +120,7 @@ class GeoModelTest(unittest.TestCase):
         qs = City.objects.all()
         self.assertRaises(TypeError, qs.kml, 'name')
 
-        # The reference KML depends on the version of PostGIS used 
+        # The reference KML depends on the version of PostGIS used
         # (the output stopped including altitude in 1.3.3).
         major, minor1, minor2 = SpatialBackend.version
         ref_kml1 = '<Point><coordinates>-104.609252,38.255001,0</coordinates></Point>'
@@ -227,7 +227,7 @@ class GeoModelTest(unittest.TestCase):
         if DISABLE: return
         # Getting Texas, yes we were a country -- once ;)
         texas = Country.objects.get(name='Texas')
-        
+
         # Seeing what cities are in Texas, should get Houston and Dallas,
         #  and Oklahoma City because 'contained' only checks on the
         #  _bounding box_ of the Geometries.
@@ -288,15 +288,15 @@ class GeoModelTest(unittest.TestCase):
         # `ST_Intersects`, so contains is used instead.
         nad_pnt = fromstr(nad_wkt, srid=nad_srid)
         if SpatialBackend.oracle:
-            tx = Country.objects.get(mpoly__contains=nad_pnt) 
+            tx = Country.objects.get(mpoly__contains=nad_pnt)
         else:
             tx = Country.objects.get(mpoly__intersects=nad_pnt)
         self.assertEqual('Texas', tx.name)
-        
+
         # Creating San Antonio.  Remember the Alamo.
         sa = City(name='San Antonio', point=nad_pnt)
         sa.save()
-        
+
         # Now verifying that San Antonio was transformed correctly
         sa = City.objects.get(name='San Antonio')
         self.assertAlmostEqual(wgs_pnt.x, sa.point.x, 6)
@@ -321,7 +321,7 @@ class GeoModelTest(unittest.TestCase):
         # Puerto Rico should be NULL (it's a commonwealth unincorporated territory)
         self.assertEqual(1, len(nullqs))
         self.assertEqual('Puerto Rico', nullqs[0].name)
-        
+
         # The valid states should be Colorado & Kansas
         self.assertEqual(2, len(validqs))
         state_names = [s.name for s in validqs]
@@ -338,18 +338,18 @@ class GeoModelTest(unittest.TestCase):
         "Testing the 'left' and 'right' lookup types."
         if DISABLE: return
         # Left: A << B => true if xmax(A) < xmin(B)
-        # Right: A >> B => true if xmin(A) > xmax(B) 
+        # Right: A >> B => true if xmin(A) > xmax(B)
         #  See: BOX2D_left() and BOX2D_right() in lwgeom_box2dfloat4.c in PostGIS source.
-        
+
         # Getting the borders for Colorado & Kansas
         co_border = State.objects.get(name='Colorado').poly
         ks_border = State.objects.get(name='Kansas').poly
 
         # Note: Wellington has an 'X' value of 174, so it will not be considered
         #  to the left of CO.
-        
+
         # These cities should be strictly to the right of the CO border.
-        cities = ['Houston', 'Dallas', 'San Antonio', 'Oklahoma City', 
+        cities = ['Houston', 'Dallas', 'San Antonio', 'Oklahoma City',
                   'Lawrence', 'Chicago', 'Wellington']
         qs = City.objects.filter(point__right=co_border)
         self.assertEqual(7, len(qs))
@@ -365,7 +365,7 @@ class GeoModelTest(unittest.TestCase):
         #  to the left of CO.
         vic = City.objects.get(point__left=co_border)
         self.assertEqual('Victoria', vic.name)
-        
+
         cities = ['Pueblo', 'Victoria']
         qs = City.objects.filter(point__left=ks_border)
         self.assertEqual(2, len(qs))
@@ -383,12 +383,12 @@ class GeoModelTest(unittest.TestCase):
     def test15_relate(self):
         "Testing the 'relate' lookup type."
         if DISABLE: return
-        # To make things more interesting, we will have our Texas reference point in 
+        # To make things more interesting, we will have our Texas reference point in
         # different SRIDs.
         pnt1 = fromstr('POINT (649287.0363174 4177429.4494686)', srid=2847)
         pnt2 = fromstr('POINT(-98.4919715741052 29.4333344025053)', srid=4326)
 
-        # Not passing in a geometry as first param shoud 
+        # Not passing in a geometry as first param shoud
         # raise a type error when initializing the GeoQuerySet
         self.assertRaises(TypeError, Country.objects.filter, mpoly__relate=(23, 'foo'))
         # Making sure the right exception is raised for the given
@@ -440,7 +440,7 @@ class GeoModelTest(unittest.TestCase):
         # Using `field_name` keyword argument in one query and specifying an
         # order in the other (which should not be used because this is
         # an aggregate method on a spatial column)
-        u1 = qs.unionagg(field_name='point') 
+        u1 = qs.unionagg(field_name='point')
         u2 = qs.order_by('name').unionagg()
         tol = 0.00001
         if SpatialBackend.oracle:
@@ -458,8 +458,8 @@ class GeoModelTest(unittest.TestCase):
         Feature(name='Point', geom=Point(1, 1)).save()
         Feature(name='LineString', geom=LineString((0, 0), (1, 1), (5, 5))).save()
         Feature(name='Polygon', geom=Polygon(LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0)))).save()
-        Feature(name='GeometryCollection', 
-                geom=GeometryCollection(Point(2, 2), LineString((0, 0), (2, 2)), 
+        Feature(name='GeometryCollection',
+                geom=GeometryCollection(Point(2, 2), LineString((0, 0), (2, 2)),
                                         Polygon(LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0))))).save()
 
         f_1 = Feature.objects.get(name='Point')
@@ -474,7 +474,7 @@ class GeoModelTest(unittest.TestCase):
         f_4 = Feature.objects.get(name='GeometryCollection')
         self.assertEqual(True, isinstance(f_4.geom, GeometryCollection))
         self.assertEqual(f_3.geom, f_4.geom[2])
-    
+
     def test19_centroid(self):
         "Testing the `centroid` GeoQuerySet method."
         if DISABLE: return
@@ -494,7 +494,7 @@ class GeoModelTest(unittest.TestCase):
                    'Texas' : fromstr('POINT (-103.002434 36.500397)', srid=4326),
                    }
         elif SpatialBackend.postgis:
-            # Using GEOSGeometry to compute the reference point on surface values 
+            # Using GEOSGeometry to compute the reference point on surface values
             # -- since PostGIS also uses GEOS these should be the same.
             ref = {'New Zealand' : Country.objects.get(name='New Zealand').mpoly.point_on_surface,
                    'Texas' : Country.objects.get(name='Texas').mpoly.point_on_surface
@@ -533,7 +533,7 @@ class GeoModelTest(unittest.TestCase):
         if DISABLE: return
         # Both 'countries' only have two geometries.
         for c in Country.objects.num_geom(): self.assertEqual(2, c.num_geom)
-        for c in City.objects.filter(point__isnull=False).num_geom(): 
+        for c in City.objects.filter(point__isnull=False).num_geom():
             # Oracle will return 1 for the number of geometries on non-collections,
             # whereas PostGIS will return None.
             if SpatialBackend.postgis: self.assertEqual(None, c.num_geom)
@@ -566,14 +566,17 @@ class GeoModelTest(unittest.TestCase):
         # All transformation SQL will need to be performed on the
         # _parent_ table.
         qs = PennsylvaniaCity.objects.transform(32128)
-        
+
         self.assertEqual(1, qs.count())
         for pc in qs: self.assertEqual(32128, pc.point.srid)
 
 from test_feeds import GeoFeedTest
+from test_regress import GeoRegressionTests
 from test_sitemaps import GeoSitemapTest
+
 def suite():
     s = unittest.TestSuite()
+    s.addTest(unittest.makeSuite(GeoRegressionTests))
     s.addTest(unittest.makeSuite(GeoModelTest))
     s.addTest(unittest.makeSuite(GeoFeedTest))
     s.addTest(unittest.makeSuite(GeoSitemapTest))
diff --git a/django/contrib/gis/tests/geoapp/tests_mysql.py b/django/contrib/gis/tests/geoapp/tests_mysql.py
index 040130c..b4e1fb9 100644
--- a/django/contrib/gis/tests/geoapp/tests_mysql.py
+++ b/django/contrib/gis/tests/geoapp/tests_mysql.py
@@ -8,7 +8,7 @@ from django.contrib.gis.geos import *
 from django.core.exceptions import ImproperlyConfigured
 
 class GeoModelTest(unittest.TestCase):
-    
+
     def test01_initial_sql(self):
         "Testing geographic initial SQL."
         # Ensuring that data was loaded from initial SQL.
@@ -38,7 +38,7 @@ class GeoModelTest(unittest.TestCase):
         new = Point(5, 23)
         nullcity.point = new
 
-        # Ensuring that the SRID is automatically set to that of the 
+        # Ensuring that the SRID is automatically set to that of the
         #  field after assignment, but before saving.
         self.assertEqual(4326, nullcity.point.srid)
         nullcity.save()
@@ -67,7 +67,7 @@ class GeoModelTest(unittest.TestCase):
 
         ns = State.objects.get(name='NullState')
         self.assertEqual(ply, ns.poly)
-        
+
         # Testing the `ogr` and `srs` lazy-geometry properties.
         if gdal.HAS_GDAL:
             self.assertEqual(True, isinstance(ns.poly.ogr, gdal.OGRGeometry))
@@ -88,7 +88,7 @@ class GeoModelTest(unittest.TestCase):
         "Testing the 'contained', 'contains', and 'bbcontains' lookup types."
         # Getting Texas, yes we were a country -- once ;)
         texas = Country.objects.get(name='Texas')
-        
+
         # Seeing what cities are in Texas, should get Houston and Dallas,
         #  and Oklahoma City because MySQL 'within' only checks on the
         #  _bounding box_ of the Geometries.
@@ -146,8 +146,8 @@ class GeoModelTest(unittest.TestCase):
         f1 = Feature(name='Point', geom=Point(1, 1))
         f2 = Feature(name='LineString', geom=LineString((0, 0), (1, 1), (5, 5)))
         f3 = Feature(name='Polygon', geom=Polygon(LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0))))
-        f4 = Feature(name='GeometryCollection', 
-                     geom=GeometryCollection(Point(2, 2), LineString((0, 0), (2, 2)), 
+        f4 = Feature(name='GeometryCollection',
+                     geom=GeometryCollection(Point(2, 2), LineString((0, 0), (2, 2)),
                                              Polygon(LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0)))))
         f1.save()
         f2.save()
@@ -166,7 +166,7 @@ class GeoModelTest(unittest.TestCase):
         f_4 = Feature.objects.get(name='GeometryCollection')
         self.assertEqual(True, isinstance(f_4.geom, GeometryCollection))
         self.assertEqual(f_3.geom, f_4.geom[2])
-    
+
     def test07_mysql_limitations(self):
         "Testing that union(), kml(), gml() raise exceptions."
         self.assertRaises(ImproperlyConfigured, City.objects.union, Point(5, 23), field_name='point')
@@ -174,9 +174,12 @@ class GeoModelTest(unittest.TestCase):
         self.assertRaises(ImproperlyConfigured, Country.objects.all().gml, field_name='mpoly')
 
 from test_feeds import GeoFeedTest
+from test_regress import GeoRegressionTests
 from test_sitemaps import GeoSitemapTest
+
 def suite():
     s = unittest.TestSuite()
+    s.addTest(unittest.makeSuite(GeoRegressionTests))
     s.addTest(unittest.makeSuite(GeoModelTest))
     s.addTest(unittest.makeSuite(GeoFeedTest))
     s.addTest(unittest.makeSuite(GeoSitemapTest))
diff --git a/django/db/models/base.py b/django/db/models/base.py
index e704db2..c02ca04 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -499,6 +499,8 @@ class Model(object):
             setattr(self, cachename, obj)
         return getattr(self, cachename)
 
+    def prepare_database_save(self, unused):
+        return self.pk
 
 
 ############################################
diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py
index 711c817..e54aaba 100644
--- a/django/db/models/expressions.py
+++ b/django/db/models/expressions.py
@@ -90,6 +90,9 @@ class ExpressionNode(tree.Node):
     def __ror__(self, other):
         return self._combine(other, self.OR, True)
 
+    def prepare_database_save(self, unused):
+        return self
+
 class F(ExpressionNode):
     """
     An expression representing the value of the given field.
diff --git a/django/db/models/sql/subqueries.py b/django/db/models/sql/subqueries.py
index d40004b..2c1da98 100644
--- a/django/db/models/sql/subqueries.py
+++ b/django/db/models/sql/subqueries.py
@@ -239,9 +239,10 @@ class UpdateQuery(Query):
         """
         from django.db.models.base import Model
         for field, model, val in values_seq:
-            # FIXME: Some sort of db_prep_* is probably more appropriate here.
-            if field.rel and isinstance(val, Model):
-                val = val.pk
+            if hasattr(val, 'prepare_database_save'):
+                val = val.prepare_database_save(field)
+            else:
+                val = field.get_db_prep_save(val)
 
             # Getting the placeholder for the field.
             if hasattr(field, 'get_placeholder'):
