Ticket #2259: 2259_r13817.diff
File 2259_r13817.diff, 11.4 KB (added by , 14 years ago) |
---|
-
django/contrib/admin/options.py
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 3b6e2b7..f1b89ba 100644
a b class ModelAdmin(BaseModelAdmin): 600 600 """ 601 601 Given a model instance save it to the database. 602 602 """ 603 obj.save( )603 obj.save(original_pk=form.initial.get(self.model._meta.pk.name)) 604 604 605 605 def save_formset(self, request, form, formset, change): 606 606 """ … … class ModelAdmin(BaseModelAdmin): 680 680 """ 681 681 opts = obj._meta 682 682 pk_value = obj._get_pk_val() 683 same_obj_url = "../%s/" % pk_value 683 684 684 685 msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj)} 685 686 if request.POST.has_key("_continue"): 686 687 self.message_user(request, msg + ' ' + _("You may edit it again below.")) 687 688 if request.REQUEST.has_key('_popup'): 688 return HttpResponseRedirect( request.path+ "?_popup=1")689 return HttpResponseRedirect(same_obj_url + "?_popup=1") 689 690 else: 690 return HttpResponseRedirect( request.path)691 return HttpResponseRedirect(same_obj_url) 691 692 elif request.POST.has_key("_saveasnew"): 692 693 msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(opts.verbose_name), 'obj': obj} 693 694 self.message_user(request, msg) 694 return HttpResponseRedirect( "../%s/" % pk_value)695 return HttpResponseRedirect(same_obj_url) 695 696 elif request.POST.has_key("_addanother"): 696 697 self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name))) 697 698 return HttpResponseRedirect("../add/") … … class ModelAdmin(BaseModelAdmin): 875 876 return self.add_view(request, form_url='../add/') 876 877 877 878 ModelForm = self.get_form(request, obj) 879 878 880 formsets = [] 879 881 if request.method == 'POST': 880 882 form = ModelForm(request.POST, request.FILES, instance=obj) -
django/db/models/base.py
diff --git a/django/db/models/base.py b/django/db/models/base.py index b3deda1..1b13a88 100644
a b class Model(object): 420 420 return getattr(self, field_name) 421 421 return getattr(self, field.attname) 422 422 423 def save(self, force_insert=False, force_update=False, using=None ):423 def save(self, force_insert=False, force_update=False, using=None, original_pk=None): 424 424 """ 425 425 Saves the current instance. Override this in a subclass if you want to 426 426 control the saving process. … … class Model(object): 428 428 The 'force_insert' and 'force_update' parameters can be used to insist 429 429 that the "save" must be an SQL insert or update (or equivalent for 430 430 non-SQL backends), respectively. Normally, they should not be set. 431 432 The 'original_pk' argument allows updating the primary key of an 433 existing row. Assuming the actual instance pk attribute has already 434 been updated to the new value, passing in the original pk will update 435 that row. 431 436 """ 432 437 if force_insert and force_update: 433 438 raise ValueError("Cannot force both insert and updating in model saving.") 434 self.save_base(using=using, force_insert=force_insert, force_update=force_update) 439 if force_insert and original_pk is not None: 440 raise ValueError("Cannot force insert and provide an original primary key.") 441 self.save_base(using=using, force_insert=force_insert, force_update=force_update, original_pk=original_pk) 435 442 436 443 save.alters_data = True 437 444 438 445 def save_base(self, raw=False, cls=None, origin=None, force_insert=False, 439 force_update=False, using=None ):446 force_update=False, using=None, original_pk=None): 440 447 """ 441 448 Does the heavy-lifting involved in saving. Subclasses shouldn't need to 442 449 override this method. It's separate from save() in order to hide the … … class Model(object): 483 490 return 484 491 485 492 if not meta.proxy: 486 non_pks = [f for f in meta.local_fields if not f.primary_key]493 fields_to_update = meta.local_fields 487 494 488 495 # First, try an UPDATE. If that doesn't update anything, do an INSERT. 489 496 pk_val = self._get_pk_val(meta) 490 497 pk_set = pk_val is not None 498 499 if original_pk is not None: 500 if not pk_set: 501 raise ValueError("Cannot update existing row primary key to None") 502 pk_val = original_pk 503 else: 504 fields_to_update = [f for f in fields_to_update if not f.primary_key] 505 491 506 record_exists = True 492 507 manager = cls._base_manager 493 508 if pk_set: … … class Model(object): 495 510 if (force_update or (not force_insert and 496 511 manager.using(using).filter(pk=pk_val).exists())): 497 512 # It does already exist, so do an UPDATE. 498 if force_update or non_pks: 499 values = [(f, None, (raw and getattr(self, f.attname) or f.pre_save(self, False))) for f in non_pks] 513 if force_update or fields_to_update: 514 values = [ 515 (f, None, (raw and getattr(self, f.attname) or f.pre_save(self, False))) 516 for f in fields_to_update 517 ] 500 518 rows = manager.using(using).filter(pk=pk_val)._update(values) 501 519 if force_update and not rows: 502 520 raise DatabaseError("Forced update did not affect any rows.") -
tests/regressiontests/admin_views/tests.py
diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py index 725369a..6de4b52 100644
a b class SaveAsTests(TestCase): 326 326 response = self.client.post('/test_admin/admin/admin_views/person/1/', post_data) 327 327 self.assertEqual(response.context['form_url'], '../add/') 328 328 329 class UpdatePrimaryKeyTests(TestCase): 330 fixtures = ['admin-views-users.xml', 'string-primary-key.xml'] 331 model_class = ModelWithStringPrimaryKey 332 model_pk = """abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 -_.!~*'() ;/?:@&=+$, <>#%" {}|\^[]`""" 333 334 def setUp(self): 335 self.client.login(username='super', password='secret') 336 self.instance = self._get_by_pk(self.model_pk) 337 338 def tearDown(self): 339 self.client.logout() 340 341 def _get_by_pk(self, pk_val): 342 return self._manager.get(pk=pk_val) 343 344 @property 345 def _manager(self): 346 return self.model_class._default_manager 347 348 def _pk_name(self): 349 return self.model_class._meta.pk.attname 350 351 def _change_url(self, pk=None): 352 pk = pk or self.instance.pk 353 return ('/test_admin/admin/admin_views/%s/%s/' 354 % (self.model_class._meta.module_name, 355 iri_to_uri(quote(pk)))) 356 357 def test_update_pk(self): 358 post_data = { 359 self._pk_name(): 'new pk value', 360 } 361 count = self._manager.count() 362 response = self.client.post(self._change_url(), data=post_data) 363 self.assertEqual(response.status_code, 302) 364 self.assertEqual(self._manager.filter(pk='new pk value').count(), 1) 365 self.assertEqual(self._manager.count(), count) 366 self.assertFalse(self._manager.filter(pk=self.model_pk).exists()) 367 368 def test_update_pk_continue_redirect(self): 369 post_data = { 370 self._pk_name(): 'new pk value', 371 '_continue': 'Save and continue editing', 372 } 373 response = self.client.post(self._change_url(), data=post_data) 374 self.assertRedirects(response, self._change_url('new pk value')) 375 329 376 class CustomModelAdminTest(AdminViewBasicTest): 330 377 urlbit = "admin2" 331 378 -
new file tests/regressiontests/model_save/models.py
diff --git a/tests/regressiontests/model_save/__init__.py b/tests/regressiontests/model_save/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/regressiontests/model_save/models.py b/tests/regressiontests/model_save/models.py new file mode 100644 index 0000000..c9848d0
- + 1 from django.db import models 2 3 class Beverage(models.Model): 4 id = models.IntegerField(primary_key=True) 5 name = models.CharField(max_length=100) 6 7 class Train(models.Model): 8 name = models.CharField(max_length=100, primary_key=True) 9 10 class TrainCar(models.Model): 11 train = models.ForeignKey(Train) 12 number = models.IntegerField() -
new file tests/regressiontests/model_save/tests.py
diff --git a/tests/regressiontests/model_save/tests.py b/tests/regressiontests/model_save/tests.py new file mode 100644 index 0000000..2e6a026
- + 1 from django.db import DatabaseError 2 from django.test import TestCase, TransactionTestCase 3 4 from models import Beverage, Train, TrainCar 5 6 class UpdatePKTests(TestCase): 7 def setUp(self): 8 self.b = Beverage.objects.create(id=1, name='Water') 9 10 def test_update_pk(self): 11 self.b.id = 2 12 self.b.save(original_pk=1) 13 self.assertEqual(Beverage.objects.count(), 1) 14 self.assertEqual(Beverage.objects.filter(id=2).count(), 1) 15 self.assertEqual(Beverage.objects.filter(id=1).count(), 0) 16 17 def test_update_pk_to_none(self): 18 """ 19 Trying to update a row to a PK of None is totally not legit. 20 21 """ 22 self.b.id = None 23 self.assertRaises(ValueError, self.b.save, original_pk=1) 24 25 def test_bogus_original_pk(self): 26 """ 27 Asking to update a row with a nonexistent original_pk results in a 28 DatabaseError (same as combining force_update with a nonexistent pk). 29 30 """ 31 self.assertRaises(DatabaseError, self.b.save, original_pk=7) 32 33 def test_force_insert_and_original_pk(self): 34 """ 35 Asking to update a row with an original_pk and force_insert is 36 nonsensical and results in a ValueError. 37 38 """ 39 self.b.id = 2 40 self.assertRaises(ValueError, self.b.save, original_pk=1, force_insert=True) 41 42 def test_force_update_and_original_pk(self): 43 """ 44 Combining force_update and original_pk is just redundant, not 45 problematic. 46 47 """ 48 self.b.id = 2 49 self.b.save(original_pk=1, force_update=True) 50 self.assertEqual(Beverage.objects.count(), 1) 51 self.assertEqual(Beverage.objects.filter(id=2).count(), 1) 52 self.assertEqual(Beverage.objects.filter(id=1).count(), 0) 53 54 def test_update_pk_cascade(self): 55 """ 56 Updating the primary key of an object that is the referent of 57 ForeignKeys needs to cascade the update to referring row, or 58 referential integrity is broken. 59 60 XXX This test currently fails because we have neither ON UPDATE CASCADE 61 support nor any emulation of it. 62 63 """ 64 train = Train.objects.create(name='California Zephyr') 65 car = TrainCar.objects.create(train=train, number=12) 66 67 train.name = 'Empire Builder' 68 train.save(original_pk='California Zephyr') 69 car = TrainCar.objects.get(pk=car.pk) 70 self.assertEqual(car.train.name, 'Empire Builder')