diff --git a/django/db/models/base.py b/django/db/models/base.py index 13ff7e8f35..325e8764f1 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -411,29 +411,35 @@ class Model(object): save.alters_data = True - def save_base(self, raw=False, cls=None, force_insert=False, - force_update=False): + def save_base(self, raw=False, cls=None, origin=None, + force_insert=False, force_update=False): """ Does the heavy-lifting involved in saving. Subclasses shouldn't need to override this method. It's separate from save() in order to hide the need for overrides of save() to pass around internal-only parameters - ('raw' and 'cls'). + ('raw', 'cls', and 'origin'). """ assert not (force_insert and force_update) - if not cls: + if cls is None: cls = self.__class__ - meta = self._meta - signal = True - signals.pre_save.send(sender=self.__class__, instance=self, raw=raw) + meta = cls._meta + if not meta.proxy: + origin = cls else: meta = cls._meta - signal = False + + if origin: + signals.pre_save.send(sender=origin, instance=self, raw=raw) # If we are in a raw save, save the object exactly as presented. # That means that we don't try to be smart about saving attributes # that might have come from the parent class - we just save the # attributes we have been given to the class we have been given. if not raw: + if meta.proxy: + org = cls + else: + org = None for parent, field in meta.parents.items(): # At this point, parent's primary key field may be unknown # (for example, from administration form which doesn't fill @@ -441,7 +447,8 @@ class Model(object): if field and getattr(self, parent._meta.pk.attname) is None and getattr(self, field.attname) is not None: setattr(self, parent._meta.pk.attname, getattr(self, field.attname)) - self.save_base(cls=parent) + self.save_base(cls=parent, origin=org) + if field: setattr(self, field.attname, self._get_pk_val(parent._meta)) if meta.proxy: @@ -492,8 +499,8 @@ class Model(object): setattr(self, meta.pk.attname, result) transaction.commit_unless_managed() - if signal: - signals.post_save.send(sender=self.__class__, instance=self, + if origin: + signals.post_save.send(sender=origin, instance=self, created=(not record_exists), raw=raw) save_base.alters_data = True diff --git a/tests/modeltests/proxy_models/models.py b/tests/modeltests/proxy_models/models.py index 4b3f7d925d..4074a323ae 100644 --- a/tests/modeltests/proxy_models/models.py +++ b/tests/modeltests/proxy_models/models.py @@ -259,6 +259,33 @@ FieldError: Proxy model 'NoNewFields' contains model fields. >>> OtherPerson._default_manager.all() [, ] +# Test save signals for proxy models +>>> from django.db.models import signals +>>> def make_handler(model, event): +... def _handler(*args, **kwargs): +... print u"%s %s save" % (model, event) +... return _handler +>>> h1 = make_handler('MyPerson', 'pre') +>>> h2 = make_handler('MyPerson', 'post') +>>> h3 = make_handler('Person', 'pre') +>>> h4 = make_handler('Person', 'post') +>>> signals.pre_save.connect(h1, sender=MyPerson) +>>> signals.post_save.connect(h2, sender=MyPerson) +>>> signals.pre_save.connect(h3, sender=Person) +>>> signals.post_save.connect(h4, sender=Person) +>>> dino = MyPerson.objects.create(name=u"dino") +MyPerson pre save +MyPerson post save + +# Test save signals for proxy proxy models +>>> h5 = make_handler('MyPersonProxy', 'pre') +>>> h6 = make_handler('MyPersonProxy', 'post') +>>> signals.pre_save.connect(h5, sender=MyPersonProxy) +>>> signals.post_save.connect(h6, sender=MyPersonProxy) +>>> dino = MyPersonProxy.objects.create(name=u"pebbles") +MyPersonProxy pre save +MyPersonProxy post save + # A proxy has the same content type as the model it is proxying for (at the # storage level, it is meant to be essentially indistinguishable). >>> ctype = ContentType.objects.get_for_model @@ -266,7 +293,7 @@ FieldError: Proxy model 'NoNewFields' contains model fields. True >>> MyPersonProxy.objects.all() -[, ] +[, , , ] >>> u = User.objects.create(name='Bruce') >>> User.objects.all()