Ticket #2259: updatable_pk.diff
File updatable_pk.diff, 15.0 KB (added by , 13 years ago) |
---|
-
django/contrib/admin/options.py
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index cf7ea83..81a7772 100644
a b class ModelAdmin(BaseModelAdmin): 793 793 verbose_name = opts_.verbose_name 794 794 795 795 pk_value = obj._get_pk_val() 796 same_obj_url = "../%s/" % pk_value 796 797 797 798 msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': force_unicode(verbose_name), 'obj': force_unicode(obj)} 798 799 if "_continue" in request.POST: 799 800 self.message_user(request, msg + ' ' + _("You may edit it again below.")) 800 801 if "_popup" in request.REQUEST: 801 return HttpResponseRedirect( request.path+ "?_popup=1")802 return HttpResponseRedirect(same_obj_url + "?_popup=1") 802 803 else: 803 return HttpResponseRedirect( request.path)804 return HttpResponseRedirect(same_obj_url) 804 805 elif "_saveasnew" in request.POST: 805 806 msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(verbose_name), 'obj': obj} 806 807 self.message_user(request, msg) 807 return HttpResponseRedirect( "../%s/" % pk_value)808 return HttpResponseRedirect(same_obj_url) 808 809 elif "_addanother" in request.POST: 809 810 self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(verbose_name))) 810 811 return HttpResponseRedirect("../add/") -
django/db/models/base.py
diff --git a/django/db/models/base.py b/django/db/models/base.py index 71fd1f7..ddf7f4c 100644
a b class ModelState(object): 271 271 # Necessary for correct validation of new instances of objects with explicit (non-auto) PKs. 272 272 # This impacts validation only; it has no effect on the actual save. 273 273 self.adding = True 274 # This if set when an object is fetched from the database and used in saving when the pk 275 # has been changed. 276 self.original_pk = None 274 277 275 278 class Model(object): 276 279 __metaclass__ = ModelBase … … class Model(object): 520 523 pk_set = pk_val is not None 521 524 record_exists = True 522 525 manager = cls._base_manager 526 original_pk = self._state.original_pk 527 pk_changed = original_pk is not None and original_pk <> pk_val 528 use_original_pk = False 523 529 if pk_set: 524 # Determine whether a record with the primary key already exists. 525 if (force_update or (not force_insert and 526 manager.using(using).filter(pk=pk_val).exists())): 527 # It does already exist, so do an UPDATE. 530 if force_insert: 531 record_exists = False 532 # Determine whether a record with our current primary key already exists. 533 elif (force_update and not pk_changed) or manager.using(using).filter(pk=pk_val).exists(): 534 # It does already exist, so we are UPDATEing. 535 536 # If original PK is different from current PK, then we are trying to change 537 # our PK to an already existing one. This is an error if force_update 538 # is not set. 539 if pk_changed and not force_update: 540 raise DatabaseError("Trying to change the primary key to already existing value. If this is wanted, use force_update") 528 541 if force_update or non_pks: 529 542 values = [(f, None, (raw and getattr(self, f.attname) or f.pre_save(self, False))) for f in non_pks] 530 543 rows = manager.using(using).filter(pk=pk_val)._update(values) 531 544 if force_update and not rows: 532 545 raise DatabaseError("Forced update did not affect any rows.") 546 if not rows: 547 raise DatabaseError('And existing row with pk "%s" was changed concurrently' % pk_val) 548 # There was no record with our current primary key. Lets see if our primary 549 # key has changed, and there is a record with the original primary key. 550 elif pk_changed and manager.using(using).filter(pk=original_pk).exists(): 551 # So we are changing pk and the old one exists still 552 # in the DB. Lets update it. 553 # TODO: related object handling. 554 values = [(f, None, (raw and getattr(self, f.attname) or f.pre_save(self, False))) for f in meta.local_fields] 555 rows = manager.using(using).filter(pk=original_pk)._update(values) 556 # TODO: I am could be a little more DRY 557 if force_update and not rows: 558 raise DatabaseError("Forced update did not affect any rows.") 559 if not rows: 560 raise DatabaseError('An existing row with pk %s was changed concurrently.' % original_pk) 561 # Neither the old nor the new pk was found from the DB. 562 elif force_update: 563 raise DatabaseError("Forced update did not affect any rows.") 533 564 else: 534 565 record_exists = False 535 566 if not pk_set or not record_exists: … … class Model(object): 560 591 result = manager._insert([(meta.pk, connection.ops.pk_default_value())], return_id=update_pk, raw_values=True, using=using) 561 592 562 593 if update_pk: 594 pk_val = result 563 595 setattr(self, meta.pk.attname, result) 564 596 transaction.commit_unless_managed(using=using) 565 597 … … class Model(object): 567 599 self._state.db = using 568 600 # Once saved, this is no longer a to-be-added instance. 569 601 self._state.adding = False 602 self._state.original_pk = pk_val 570 603 571 604 # Signal that the save is complete 572 605 if origin and not meta.auto_created: -
django/db/models/query.py
diff --git a/django/db/models/query.py b/django/db/models/query.py index ff5289c..88fcc38 100644
a b class QuerySet(object): 281 281 obj._state.db = db 282 282 # This object came from the database; it's not being added. 283 283 obj._state.adding = False 284 obj._state.original_pk = obj.pk 284 285 285 286 if extra_select: 286 287 for i, k in enumerate(extra_select): … … def get_cached_row(klass, row, index_start, using, max_depth=0, cur_depth=0, 1231 1232 if obj: 1232 1233 obj._state.db = using 1233 1234 obj._state.adding = False 1235 obj._state.original_pk = obj.pk 1234 1236 1235 1237 index_end = index_start + field_count + offset 1236 1238 # Iterate over each related object, populating any … … class RawQuerySet(object): 1381 1383 1382 1384 instance._state.db = db 1383 1385 instance._state.adding = False 1386 instance._state.original_pk = instance.pk 1384 1387 1385 1388 yield instance 1386 1389 -
django/db/models/query_utils.py
diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py index a56ab5c..42f088d 100644
a b class DeferredAttribute(object): 95 95 # We use only() instead of values() here because we want the 96 96 # various data coersion methods (to_python(), etc.) to be called 97 97 # here. 98 val = getattr( 99 cls._base_manager.filter(pk=instance.pk).only(name).using( 100 instance._state.db).get(), 101 self.field_name 102 ) 98 try: 99 val = getattr( 100 cls._base_manager.filter(pk=instance.pk).only(name).using( 101 instance._state.db).get(), 102 self.field_name 103 ) 104 except cls.DoesNotExist, e: 105 if (instance._state.original_pk and 106 instance._state.original_pk <> instance.pk): 107 val = getattr( 108 cls._base_manager.filter(pk=instance._state.original_pk).only(name).using( 109 instance._state.db).get(), 110 self.field_name 111 ) 112 else: 113 raise e 103 114 data[self.field_name] = val 104 115 return data[self.field_name] 105 116 -
tests/modeltests/custom_pk/tests.py
diff --git a/tests/modeltests/custom_pk/tests.py b/tests/modeltests/custom_pk/tests.py index 6b94b6d..b07e91c 100644
a b 1 1 # -*- coding: utf-8 -*- 2 from django.db import transaction, IntegrityError 2 from django.db import transaction, IntegrityError, DatabaseError 3 3 from django.test import TestCase, skipIfDBFeature 4 4 5 5 from models import Employee, Business, Bar, Foo … … class CustomPKTests(TestCase): 35 35 lambda: Employee.objects.get(pk=42) 36 36 ) 37 37 38 # lets change Fran's employee_code, and see that the old row has 39 # been changed. 40 fran.employee_code = 42 41 fran.save() 42 self.assertEqual(Employee.objects.get(pk=42), fran) 43 self.assertEqual(2, Employee.objects.count()) 44 # Change the employee code back. 45 fran.employee_code = 456 46 fran.save() 47 self.assertEqual(Employee.objects.get(pk=456), fran) 48 self.assertEqual(2, Employee.objects.count()) 49 # Lets see what happens when we take Fran from queryset 50 # and change pk. 51 fran2 = Employee.objects.get(pk=456) 52 fran2.employee_code = 42 53 fran2.save() 54 self.assertEqual(Employee.objects.get(pk=42), fran2) 55 self.assertEqual(2, Employee.objects.count()) 56 fran2.employee_code = 456 57 fran2.save() 58 # fran2 can also come from a raw query. 59 fran2 = Employee.objects.raw(""" 60 select code 61 from custom_pk_employee 62 where code = 456 63 """)[0] 64 fran2.employee_code = 42 65 fran2.save() 66 # Deferred fran is not equivalent to fran, so check names 67 self.assertEqual(Employee.objects.get(pk=42).first_name, fran2.first_name) 68 self.assertEqual(2, Employee.objects.count()) 69 fran2.employee_code = 456 70 fran2.save() 71 72 # Lets see what happens if we try to save fran as dan 73 try: 74 fran.employee_code = 123 75 fran.save() 76 # Probably a better way exists, but there should 77 # have been an exception... 78 self.assertTrue(False) 79 except DatabaseError: 80 pass 81 # lets force_insert fran as 234 82 fran.employee_code = 234 83 fran.save(force_insert=True) 84 # now we have duplicated fran. 85 self.assertEqual(3, Employee.objects.count()) 86 self.assertEqual(Employee.objects.get(pk=234), fran) 87 # we can force_update dan to be fran 88 dan.employee_code = 234 89 dan.save(force_update=True) 90 # The old dan did not go anywhere, we forcefully updated 91 # the row with pk=234, but the old record is not deleted. 92 self.assertEqual(3, Employee.objects.count()) 93 self.assertEqual( 94 Employee.objects.get(pk=234).first_name, 95 Employee.objects.get(pk=123).first_name 96 ) 97 dan.delete() 98 dan = Employee.objects.get(pk=123) 99 100 101 102 103 38 104 # Use the name of the primary key, rather than pk. 39 105 self.assertEqual(Employee.objects.get(employee_code=123), dan) 40 106 # pk can be used as a substitute for the primary key. -
tests/modeltests/one_to_one/tests.py
diff --git a/tests/modeltests/one_to_one/tests.py b/tests/modeltests/one_to_one/tests.py index c3e1704..cbd6389 100644
a b class OneToOneTests(TestCase): 24 24 # Set the place using assignment notation. Because place is the primary 25 25 # key on Restaurant, the save will create a new restaurant 26 26 self.r.place = self.p2 27 self.r.save( )27 self.r.save(force_insert=True) 28 28 self.assertEqual(repr(self.p2.restaurant), '<Restaurant: Ace Hardware the restaurant>') 29 29 self.assertEqual(repr(self.r.place), '<Place: Ace Hardware the place>') 30 30 self.assertEqual(self.p2.pk, self.r.pk) -
tests/modeltests/signals/tests.py
diff --git a/tests/modeltests/signals/tests.py b/tests/modeltests/signals/tests.py index 9b8bce0..12967a0 100644
a b class SignalTests(TestCase): 123 123 ]) 124 124 data[:] = [] 125 125 126 127 p2 = Person(first_name="James", last_name="Jones") 126 128 p2.id = 99998 127 129 p2.save() 128 130 self.assertEqual(data, [ -
tests/regressiontests/admin_views/tests.py
diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py index 8daa466..098aabf 100644
a b class SaveAsTests(TestCase): 587 587 response = self.client.post('/test_admin/admin/admin_views/person/1/', post_data) 588 588 self.assertEqual(response.context['form_url'], '../add/') 589 589 590 class UpdatePrimaryKeyTests(TestCase): 591 fixtures = ['admin-views-users.xml', 'string-primary-key.xml'] 592 model_class = ModelWithStringPrimaryKey 593 model_pk = """abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 -_.!~*'() ;/?:@&=+$, <>#%" {}|\^[]`""" 594 595 def setUp(self): 596 self.client.login(username='super', password='secret') 597 self.instance = self._get_by_pk(self.model_pk) 598 599 def tearDown(self): 600 self.client.logout() 601 602 def _get_by_pk(self, pk_val): 603 return self._manager.get(pk=pk_val) 604 605 @property 606 def _manager(self): 607 return self.model_class._default_manager 608 609 def _pk_name(self): 610 return self.model_class._meta.pk.attname 611 612 def _change_url(self, pk=None): 613 pk = pk or self.instance.pk 614 return ('/test_admin/admin/admin_views/%s/%s/' 615 % (self.model_class._meta.module_name, 616 iri_to_uri(quote(pk)))) 617 618 def test_update_pk(self): 619 post_data = { 620 self._pk_name(): 'new pk value', 621 } 622 count = self._manager.count() 623 response = self.client.post(self._change_url(), data=post_data) 624 self.assertEqual(response.status_code, 302) 625 self.assertEqual(self._manager.filter(pk='new pk value').count(), 1) 626 self.assertEqual(self._manager.count(), count) 627 self.assertFalse(self._manager.filter(pk=self.model_pk).exists()) 628 629 def test_update_pk_continue_redirect(self): 630 post_data = { 631 self._pk_name(): 'new pk value', 632 '_continue': 'Save and continue editing', 633 } 634 response = self.client.post(self._change_url(), data=post_data) 635 self.assertRedirects(response, self._change_url('new pk value')) 636 590 637 class CustomModelAdminTest(AdminViewBasicTest): 591 638 urlbit = "admin2" 592 639