diff --git a/django/__init__.py b/django/__init__.py index 0d28bb534900..7525ecb60426 100644 --- a/django/__init__.py +++ b/django/__init__.py @@ -1,6 +1,6 @@ from django.utils.version import get_version -VERSION = (4, 2, 6, "+hs", 2) +VERSION = (4, 2, 6, "+hs", 3) __version__ = get_version(VERSION) diff --git a/django/db/models/base.py b/django/db/models/base.py index 15c3adc6dbd3..1c40d095ea9a 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -462,14 +462,17 @@ class ModelState: class Model(AltersData, metaclass=ModelBase): def __init__(self, *args, **kwargs): # Alias some things as locals to avoid repeat global lookups - cls = self.__class__ + cls = sender = self.__class__ opts = self._meta _setattr = setattr _DEFERRED = DEFERRED if opts.abstract: raise TypeError("Abstract models cannot be instantiated.") - pre_init.send(sender=cls, args=args, kwargs=kwargs) + # CORE-3229: Forward signals from proxy models to their concrete models + if sender._meta.proxy: + sender = sender._meta.concrete_model + pre_init.send(sender=sender, args=args, kwargs=kwargs) # Set up the storage for instance state self._state = ModelState() @@ -572,7 +575,7 @@ def __init__(self, *args, **kwargs): f"{unexpected_names}" ) super().__init__() - post_init.send(sender=cls, instance=self) + post_init.send(sender=sender, instance=self) @classmethod def from_db(cls, db, field_names, values): @@ -855,14 +858,14 @@ def save_base( using = using or router.db_for_write(self.__class__, instance=self) assert not (force_insert and (force_update or update_fields)) assert update_fields is None or update_fields - cls = origin = self.__class__ - # Skip proxies, but keep the origin as the proxy model. + cls = self.__class__ + # CORE-3229: Forward signals from proxy models to their concrete models if cls._meta.proxy: cls = cls._meta.concrete_model meta = cls._meta if not meta.auto_created: pre_save.send( - sender=origin, + sender=cls, instance=self, raw=raw, using=using, @@ -893,7 +896,7 @@ def save_base( # Signal that the save is complete if not meta.auto_created: post_save.send( - sender=origin, + sender=cls, instance=self, created=(not updated), update_fields=update_fields, diff --git a/django/db/models/deletion.py b/django/db/models/deletion.py index bc26d82e934c..8f2ae020d528 100644 --- a/django/db/models/deletion.py +++ b/django/db/models/deletion.py @@ -460,6 +460,10 @@ def delete(self): # send pre_delete signals for model, obj in self.instances_with_model(): if not model._meta.auto_created: + # CORE-3229: Forward signals from proxy models to their concrete + # models + if model._meta.proxy: + model = model._meta.concrete_model signals.pre_delete.send( sender=model, instance=obj, @@ -508,6 +512,10 @@ def delete(self): deleted_counter[model._meta.label] += count if not model._meta.auto_created: + # CORE-3229: Forward signals from proxy models to their concrete + # models + if model._meta.proxy: + model = model._meta.concrete_model for obj in instances: signals.post_delete.send( sender=model,