Skip to content

Commit

Permalink
Fixed #10672 -- Altered save_base to ensure that proxy models send a …
Browse files Browse the repository at this point in the history
…post_save signal.

git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/http-wsgi-improvements@11001 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
ccahoon committed Jun 13, 2009
1 parent 6ff1f9c commit 1015dad
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 12 deletions.
29 changes: 18 additions & 11 deletions django/db/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,37 +411,44 @@ def save(self, force_insert=False, force_update=False):

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
# this field). If so, fill it.
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:
Expand Down Expand Up @@ -492,8 +499,8 @@ def save_base(self, raw=False, cls=None, force_insert=False,
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
Expand Down
29 changes: 28 additions & 1 deletion tests/modeltests/proxy_models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,14 +259,41 @@ class Meta:
>>> OtherPerson._default_manager.all()
[<OtherPerson: barney>, <OtherPerson: wilma>]
# 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
>>> ctype(Person) is ctype(OtherPerson)
True
>>> MyPersonProxy.objects.all()
[<MyPersonProxy: barney>, <MyPersonProxy: fred>]
[<MyPersonProxy: barney>, <MyPersonProxy: dino>, <MyPersonProxy: fred>, <MyPersonProxy: pebbles>]
>>> u = User.objects.create(name='Bruce')
>>> User.objects.all()
Expand Down

0 comments on commit 1015dad

Please sign in to comment.