diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index cf7ea83..81a7772 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -793,18 +793,19 @@ class ModelAdmin(BaseModelAdmin):
             verbose_name = opts_.verbose_name
 
         pk_value = obj._get_pk_val()
+        same_obj_url = "../%s/" % pk_value 
 
         msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': force_unicode(verbose_name), 'obj': force_unicode(obj)}
         if "_continue" in request.POST:
             self.message_user(request, msg + ' ' + _("You may edit it again below."))
             if "_popup" in request.REQUEST:
-                return HttpResponseRedirect(request.path + "?_popup=1")
+                return HttpResponseRedirect(same_obj_url + "?_popup=1")
             else:
-                return HttpResponseRedirect(request.path)
+                return HttpResponseRedirect(same_obj_url)
         elif "_saveasnew" in request.POST:
             msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(verbose_name), 'obj': obj}
             self.message_user(request, msg)
-            return HttpResponseRedirect("../%s/" % pk_value)
+            return HttpResponseRedirect(same_obj_url)
         elif "_addanother" in request.POST:
             self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(verbose_name)))
             return HttpResponseRedirect("../add/")
diff --git a/django/db/models/base.py b/django/db/models/base.py
index 71fd1f7..ddf7f4c 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -271,6 +271,9 @@ class ModelState(object):
         # Necessary for correct validation of new instances of objects with explicit (non-auto) PKs.
         # This impacts validation only; it has no effect on the actual save.
         self.adding = True
+        # This if set when an object is fetched from the database and used in saving when the pk
+        # has been changed.
+        self.original_pk = None
 
 class Model(object):
     __metaclass__ = ModelBase
@@ -520,16 +523,44 @@ class Model(object):
             pk_set = pk_val is not None
             record_exists = True
             manager = cls._base_manager
+            original_pk = self._state.original_pk
+            pk_changed = original_pk is not None and original_pk <> pk_val
+            use_original_pk = False
             if pk_set:
-                # Determine whether a record with the primary key already exists.
-                if (force_update or (not force_insert and
-                        manager.using(using).filter(pk=pk_val).exists())):
-                    # It does already exist, so do an UPDATE.
+                if force_insert:
+                    record_exists = False
+                # Determine whether a record with our current primary key already exists.
+                elif (force_update and not pk_changed) or manager.using(using).filter(pk=pk_val).exists():
+                    # It does already exist, so we are UPDATEing.
+                    
+                    # If original PK is different from current PK, then we are trying to change
+                    # our PK to an already existing one. This is an error if force_update
+                    # is not set.
+                    if pk_changed and not force_update:
+                        raise DatabaseError("Trying to change the primary key to already existing value. If this is wanted, use force_update")  
                     if force_update or non_pks:
                         values = [(f, None, (raw and getattr(self, f.attname) or f.pre_save(self, False))) for f in non_pks]
                         rows = manager.using(using).filter(pk=pk_val)._update(values)
                         if force_update and not rows:
                             raise DatabaseError("Forced update did not affect any rows.")
+                        if not rows:
+                            raise DatabaseError('And existing row with pk "%s" was changed concurrently' % pk_val)
+                # There was no record with our current primary key. Lets see if our primary
+                # key has changed, and there is a record with the original primary key.
+                elif pk_changed and manager.using(using).filter(pk=original_pk).exists():
+                    # So we are changing pk and the old one exists still
+                    # in the DB. Lets update it.
+                    # TODO: related object handling.
+                    values = [(f, None, (raw and getattr(self, f.attname) or f.pre_save(self, False))) for f in meta.local_fields]
+                    rows = manager.using(using).filter(pk=original_pk)._update(values)
+                    # TODO: I am could be a little more DRY
+                    if force_update and not rows:
+                        raise DatabaseError("Forced update did not affect any rows.")
+                    if not rows:
+                        raise DatabaseError('An existing row with pk %s was changed concurrently.' % original_pk)
+                # Neither the old nor the new pk was found from the DB.
+                elif force_update:
+                    raise DatabaseError("Forced update did not affect any rows.")
                 else:
                     record_exists = False
             if not pk_set or not record_exists:
@@ -560,6 +591,7 @@ class Model(object):
                     result = manager._insert([(meta.pk, connection.ops.pk_default_value())], return_id=update_pk, raw_values=True, using=using)
 
                 if update_pk:
+                    pk_val = result
                     setattr(self, meta.pk.attname, result)
             transaction.commit_unless_managed(using=using)
 
@@ -567,6 +599,7 @@ class Model(object):
         self._state.db = using
         # Once saved, this is no longer a to-be-added instance.
         self._state.adding = False
+        self._state.original_pk = pk_val
 
         # Signal that the save is complete
         if origin and not meta.auto_created:
diff --git a/django/db/models/query.py b/django/db/models/query.py
index ff5289c..88fcc38 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -281,6 +281,7 @@ class QuerySet(object):
                 obj._state.db = db
                 # This object came from the database; it's not being added.
                 obj._state.adding = False
+                obj._state.original_pk = obj.pk
 
             if extra_select:
                 for i, k in enumerate(extra_select):
@@ -1231,6 +1232,7 @@ def get_cached_row(klass, row, index_start, using, max_depth=0, cur_depth=0,
     if obj:
         obj._state.db = using
         obj._state.adding = False
+        obj._state.original_pk = obj.pk
 
     index_end = index_start + field_count + offset
     # Iterate over each related object, populating any
@@ -1381,6 +1383,7 @@ class RawQuerySet(object):
 
             instance._state.db = db
             instance._state.adding = False
+            instance._state.original_pk = instance.pk
 
             yield instance
 
diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py
index a56ab5c..42f088d 100644
--- a/django/db/models/query_utils.py
+++ b/django/db/models/query_utils.py
@@ -95,11 +95,22 @@ class DeferredAttribute(object):
             # We use only() instead of values() here because we want the
             # various data coersion methods (to_python(), etc.) to be called
             # here.
-            val = getattr(
-                cls._base_manager.filter(pk=instance.pk).only(name).using(
-                    instance._state.db).get(),
-                self.field_name
-            )
+            try:
+                val = getattr(
+                    cls._base_manager.filter(pk=instance.pk).only(name).using(
+                        instance._state.db).get(),
+                    self.field_name
+                )
+            except cls.DoesNotExist, e:
+                if (instance._state.original_pk and 
+                        instance._state.original_pk <> instance.pk):
+                    val = getattr(
+                        cls._base_manager.filter(pk=instance._state.original_pk).only(name).using(
+                            instance._state.db).get(),
+                        self.field_name
+                    )
+                else:
+                    raise e
             data[self.field_name] = val
         return data[self.field_name]
 
diff --git a/tests/modeltests/custom_pk/tests.py b/tests/modeltests/custom_pk/tests.py
index 6b94b6d..b07e91c 100644
--- a/tests/modeltests/custom_pk/tests.py
+++ b/tests/modeltests/custom_pk/tests.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-from django.db import transaction, IntegrityError
+from django.db import transaction, IntegrityError, DatabaseError
 from django.test import TestCase, skipIfDBFeature
 
 from models import Employee, Business, Bar, Foo
@@ -35,6 +35,72 @@ class CustomPKTests(TestCase):
             lambda: Employee.objects.get(pk=42)
         )
 
+        # lets change Fran's employee_code, and see that the old row has
+        # been changed.
+        fran.employee_code = 42
+        fran.save()
+        self.assertEqual(Employee.objects.get(pk=42), fran)
+        self.assertEqual(2, Employee.objects.count())
+        # Change the employee code back.
+        fran.employee_code = 456
+        fran.save()
+        self.assertEqual(Employee.objects.get(pk=456), fran)
+        self.assertEqual(2, Employee.objects.count())
+        # Lets see what happens when we take Fran from queryset
+        # and change pk.
+        fran2 = Employee.objects.get(pk=456)
+        fran2.employee_code = 42
+        fran2.save()
+        self.assertEqual(Employee.objects.get(pk=42), fran2)
+        self.assertEqual(2, Employee.objects.count())
+        fran2.employee_code = 456
+        fran2.save()
+        # fran2 can also come from a raw query.
+        fran2 = Employee.objects.raw("""
+            select code 
+              from custom_pk_employee 
+             where code = 456
+        """)[0]
+        fran2.employee_code = 42
+        fran2.save()
+        # Deferred fran is not equivalent to fran, so check names
+        self.assertEqual(Employee.objects.get(pk=42).first_name, fran2.first_name)
+        self.assertEqual(2, Employee.objects.count())
+        fran2.employee_code = 456
+        fran2.save()
+        
+        # Lets see what happens if we try to save fran as dan
+        try:
+            fran.employee_code = 123
+            fran.save()
+            # Probably a better way exists, but there should
+            # have been an exception...
+            self.assertTrue(False) 
+        except DatabaseError:
+            pass
+        # lets force_insert fran as 234
+        fran.employee_code = 234
+        fran.save(force_insert=True)
+        # now we have duplicated fran.
+        self.assertEqual(3, Employee.objects.count())
+        self.assertEqual(Employee.objects.get(pk=234), fran)
+        # we can force_update dan to be fran
+        dan.employee_code = 234
+        dan.save(force_update=True)
+        # The old dan did not go anywhere, we forcefully updated
+        # the row with pk=234, but the old record is not deleted.
+        self.assertEqual(3, Employee.objects.count())
+        self.assertEqual(
+            Employee.objects.get(pk=234).first_name,
+            Employee.objects.get(pk=123).first_name
+        )
+        dan.delete()
+        dan = Employee.objects.get(pk=123)
+        
+     
+
+
+
         # Use the name of the primary key, rather than pk.
         self.assertEqual(Employee.objects.get(employee_code=123), dan)
         # pk can be used as a substitute for the primary key.
diff --git a/tests/modeltests/one_to_one/tests.py b/tests/modeltests/one_to_one/tests.py
index c3e1704..cbd6389 100644
--- a/tests/modeltests/one_to_one/tests.py
+++ b/tests/modeltests/one_to_one/tests.py
@@ -24,7 +24,7 @@ class OneToOneTests(TestCase):
         # Set the place using assignment notation. Because place is the primary
         # key on Restaurant, the save will create a new restaurant
         self.r.place = self.p2
-        self.r.save()
+        self.r.save(force_insert=True)
         self.assertEqual(repr(self.p2.restaurant), '<Restaurant: Ace Hardware the restaurant>')
         self.assertEqual(repr(self.r.place), '<Place: Ace Hardware the place>')
         self.assertEqual(self.p2.pk, self.r.pk)
diff --git a/tests/modeltests/signals/tests.py b/tests/modeltests/signals/tests.py
index 9b8bce0..12967a0 100644
--- a/tests/modeltests/signals/tests.py
+++ b/tests/modeltests/signals/tests.py
@@ -123,6 +123,8 @@ class SignalTests(TestCase):
         ])
         data[:] = []
 
+        
+        p2 = Person(first_name="James", last_name="Jones")
         p2.id = 99998
         p2.save()
         self.assertEqual(data, [
diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py
index 8daa466..098aabf 100644
--- a/tests/regressiontests/admin_views/tests.py
+++ b/tests/regressiontests/admin_views/tests.py
@@ -587,6 +587,53 @@ class SaveAsTests(TestCase):
         response = self.client.post('/test_admin/admin/admin_views/person/1/', post_data)
         self.assertEqual(response.context['form_url'], '../add/')
 
+class UpdatePrimaryKeyTests(TestCase):
+    fixtures = ['admin-views-users.xml', 'string-primary-key.xml']
+    model_class = ModelWithStringPrimaryKey
+    model_pk = """abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 -_.!~*'() ;/?:@&=+$, <>#%" {}|\^[]`"""
+
+    def setUp(self):
+        self.client.login(username='super', password='secret')
+        self.instance = self._get_by_pk(self.model_pk)
+
+    def tearDown(self):
+        self.client.logout()
+
+    def _get_by_pk(self, pk_val):
+        return self._manager.get(pk=pk_val)
+
+    @property
+    def _manager(self):
+        return self.model_class._default_manager
+
+    def _pk_name(self):
+        return self.model_class._meta.pk.attname
+
+    def _change_url(self, pk=None):
+        pk = pk or self.instance.pk
+        return ('/test_admin/admin/admin_views/%s/%s/'
+                % (self.model_class._meta.module_name,
+                   iri_to_uri(quote(pk))))
+
+    def test_update_pk(self):
+        post_data = {
+            self._pk_name(): 'new pk value',
+            }
+        count = self._manager.count()
+        response = self.client.post(self._change_url(), data=post_data)
+        self.assertEqual(response.status_code, 302)
+        self.assertEqual(self._manager.filter(pk='new pk value').count(), 1)
+        self.assertEqual(self._manager.count(), count)
+        self.assertFalse(self._manager.filter(pk=self.model_pk).exists())
+
+    def test_update_pk_continue_redirect(self):
+        post_data = {
+            self._pk_name(): 'new pk value',
+            '_continue': 'Save and continue editing',
+            }
+        response = self.client.post(self._change_url(), data=post_data)
+        self.assertRedirects(response, self._change_url('new pk value'))
+ 
 class CustomModelAdminTest(AdminViewBasicTest):
     urlbit = "admin2"
 
