Ticket #17341: 17341.2.diff
File 17341.2.diff, 11.0 KB (added by , 13 years ago) |
---|
-
django/db/models/base.py
diff --git a/django/db/models/base.py b/django/db/models/base.py index ebd67be..ee3df5e 100644
a b class Model(object): 465 465 466 466 save.alters_data = True 467 467 468 def save_base(self, raw=False, cls=None, origin=None, force_insert=False,469 force_update=False, using=None):468 def save_base(self, raw=False, cls=None, topmost_concrete=True, 469 force_insert=False, force_update=False, using=None): 470 470 """ 471 471 Does the heavy-lifting involved in saving. Subclasses shouldn't need to 472 472 override this method. It's separate from save() in order to hide the 473 473 need for overrides of save() to pass around internal-only parameters 474 ('raw', 'cls', and 'origin'). 474 ('raw', 'topmost_concrete', and 'origin'). 475 476 Internal parameters: 477 - raw: If set, save just the table used by self.__class__. That is, do 478 not follow inheritance chains, except for proxy-skipping. 479 - topmost_concrete: Kept True as long as we are skipping the outmost 480 proxy models. For parents of a concrete model (that is multitable 481 inherited model's parents), this will be False. 482 - cls: This is the current class under save. In inheritance chains this 483 is given from the previous save_base, but in the topmost save this 484 will be given in as None. 475 485 """ 476 486 using = using or router.db_for_write(self.__class__, instance=self) 477 487 assert not (force_insert and force_update) 488 478 489 if cls is None: 479 490 cls = self.__class__ 480 meta = cls._meta 481 if not meta.proxy: 482 origin = cls 483 else: 484 meta = cls._meta 491 meta = cls._meta 492 493 # Skip proxy chains. Defer the save of proxy objects to their actual 494 # underlying model. 495 if meta.proxy: 496 parent, field = meta.parents.items()[0] 497 self.save_base(cls=parent, topmost_concrete=topmost_concrete, using=using) 498 return 485 499 486 if origin and not meta.auto_created: 487 signals.pre_save.send(sender=origin, instance=self, raw=raw, using=using) 500 if topmost_concrete and not meta.auto_created: 501 signals.pre_save.send(sender=self.__class__, instance=self, 502 raw=raw, using=using) 488 503 489 504 # If we are in a raw save, save the object exactly as presented. 490 505 # That means that we don't try to be smart about saving attributes 491 506 # that might have come from the parent class - we just save the 492 507 # attributes we have been given to the class we have been given. 493 # We also go through this process to defer the save of proxy objects 494 # to their actual underlying model. 495 if not raw or meta.proxy: 496 if meta.proxy: 497 org = cls 498 else: 499 org = None 508 if not raw: 500 509 for parent, field in meta.parents.items(): 501 510 # At this point, parent's primary key field may be unknown 502 511 # (for example, from administration form which doesn't fill 503 512 # this field). If so, fill it. 504 if field and getattr(self, parent._meta.pk.attname) is None and getattr(self, field.attname) is not None: 513 if (field and getattr(self, parent._meta.pk.attname) is None 514 and getattr(self, field.attname) is not None): 505 515 setattr(self, parent._meta.pk.attname, getattr(self, field.attname)) 506 516 507 self.save_base(cls=parent, origin=org, using=using) 508 517 self.save_base(cls=parent, topmost_concrete=False, using=using) 509 518 if field: 510 519 setattr(self, field.attname, self._get_pk_val(parent._meta)) 511 if meta.proxy:512 return513 514 if not meta.proxy:515 non_pks = [f for f in meta.local_fields if not f.primary_key]516 517 # First, try an UPDATE. If that doesn't update anything, do an INSERT.518 pk_val = self._get_pk_val(meta)519 pk_set = pk_val is not None520 record_exists = True521 manager = cls._base_manager522 if pk_set:523 # Determine whether a record with the primary key already exists.524 if (force_update or (not force_insert and525 manager.using(using).filter(pk=pk_val).exists())):526 # It does already exist, so do an UPDATE.527 if force_update or non_pks:528 values = [(f, None, (raw and getattr(self, f.attname) or f.pre_save(self, False))) for f in non_pks]529 if values:530 rows = manager.using(using).filter(pk=pk_val)._update(values)531 if force_update and not rows:532 raise DatabaseError("Forced update did not affect any rows.")533 else:534 record_exists = False535 if not pk_set or not record_exists:536 if meta.order_with_respect_to:537 # If this is a model with an order_with_respect_to538 # autopopulate the _order field539 field = meta.order_with_respect_to540 order_value = manager.using(using).filter(**{field.name: getattr(self, field.attname)}).count()541 self._order = order_value542 543 fields = meta.local_fields544 if not pk_set:545 if force_update:546 raise ValueError("Cannot force an update in save() with no primary key.")547 fields = [f for f in fields if not isinstance(f, AutoField)]548 520 521 non_pks = [f for f in meta.local_fields if not f.primary_key] 522 523 # First, try an UPDATE. If that doesn't update anything, do an INSERT. 524 pk_val = self._get_pk_val(meta) 525 pk_set = pk_val is not None 526 record_exists = True 527 manager = cls._base_manager 528 if pk_set: 529 # Determine whether a record with the primary key already exists. 530 if (force_update or (not force_insert and 531 manager.using(using).filter(pk=pk_val).exists())): 532 # It does already exist, so do an UPDATE. 533 if force_update or non_pks: 534 values = [(f, None, (raw and getattr(self, f.attname) or f.pre_save(self, False))) for f in non_pks] 535 if values: 536 rows = manager.using(using).filter(pk=pk_val)._update(values) 537 if force_update and not rows: 538 raise DatabaseError("Forced update did not affect any rows.") 539 else: 549 540 record_exists = False 541 if not pk_set or not record_exists: 542 if meta.order_with_respect_to: 543 # If this is a model with an order_with_respect_to 544 # autopopulate the _order field 545 field = meta.order_with_respect_to 546 order_value = manager.using(using).filter(**{field.name: getattr(self, field.attname)}).count() 547 self._order = order_value 550 548 551 update_pk = bool(meta.has_auto_field and not pk_set) 552 result = manager._insert([self], fields=fields, return_id=update_pk, using=using, raw=raw) 549 fields = meta.local_fields 550 if not pk_set: 551 if force_update: 552 raise ValueError("Cannot force an update in save() with no primary key.") 553 fields = [f for f in fields if not isinstance(f, AutoField)] 553 554 554 if update_pk: 555 setattr(self, meta.pk.attname, result) 556 transaction.commit_unless_managed(using=using) 555 record_exists = False 556 557 update_pk = bool(meta.has_auto_field and not pk_set) 558 result = manager._insert([self], fields=fields, return_id=update_pk, using=using, raw=raw) 559 560 if update_pk: 561 setattr(self, meta.pk.attname, result) 557 562 558 563 # Store the database on which the object was saved 559 564 self._state.db = using 560 565 # Once saved, this is no longer a to-be-added instance. 561 566 self._state.adding = False 562 567 563 # Signal that the save is complete 564 if origin and not meta.auto_created: 565 signals.post_save.send(sender=origin, instance=self, 566 created=(not record_exists), raw=raw, using=using) 568 # Commit the transaction & signal that the save is complete. 569 if topmost_concrete: 570 transaction.commit_unless_managed(using=using) 571 if not meta.auto_created: 572 signals.post_save.send(sender=self.__class__, instance=self, 573 created=(not record_exists), raw=raw, using=using) 567 574 568 575 569 576 save_base.alters_data = True -
tests/modeltests/transactions/models.py
diff --git a/tests/modeltests/transactions/models.py b/tests/modeltests/transactions/models.py index 66acb9f..b0bad3c 100644
a b class Reporter(models.Model): 19 19 ordering = ('first_name', 'last_name') 20 20 21 21 def __unicode__(self): 22 return u"%s %s" % (self.first_name, self.last_name) 23 No newline at end of file 22 return u"%s %s" % (self.first_name, self.last_name) 23 24 class BaseModel(models.Model): 25 pass 26 27 class InheritedModel(BaseModel): 28 uniq_id = models.IntegerField(unique=True) -
tests/modeltests/transactions/tests.py
diff --git a/tests/modeltests/transactions/tests.py b/tests/modeltests/transactions/tests.py index ed416e2..f4c6328 100644
a b from __future__ import with_statement, absolute_import 3 3 from django.db import connection, transaction, IntegrityError 4 4 from django.test import TransactionTestCase, skipUnlessDBFeature 5 5 6 from .models import Reporter 6 from .models import Reporter, InheritedModel, BaseModel 7 7 8 8 9 9 class TransactionTests(TransactionTestCase): … … class TransactionTests(TransactionTestCase): 160 160 using_manually_managed_mistake 161 161 ) 162 162 163 @skipUnlessDBFeature('supports_transactions') 164 def test_nonmanaged_inheritance_save(self): 165 """ 166 When running in nonmanaged mode, Django commits transactions after 167 each save. It multitable inheritance case, test that the commit is 168 done only once, after all tables have been saved. Check it by making 169 forcing an IntegrityError, and see what happens. 170 """ 171 InheritedModel(uniq_id=1).save() 172 try: 173 InheritedModel(uniq_id=1).save() 174 # Make sure there is an error! 175 self.assertFalse(True) 176 except IntegrityError: 177 pass 178 # Now, rollback the connection and see what got saved. 179 connection.rollback() 180 self.assertEquals(InheritedModel.objects.count(), 1) 181 self.assertEquals(BaseModel.objects.count(), 1) 182 163 183 164 184 class TransactionRollbackTests(TransactionTestCase): 165 185 def execute_bad_sql(self):