Loading django/core/management/validation.py +29 −1 Original line number Diff line number Diff line import collections import sys import types from django.conf import settings from django.core.management.color import color_style Loading @@ -25,7 +26,7 @@ def get_validation_errors(outfile, app=None): validates all models of all installed apps. Writes errors, if any, to outfile. Returns number of errors. """ from django.db import models, connection from django.db import connection, models from django.db.models.loading import get_app_errors from django.db.models.deletion import SET_NULL, SET_DEFAULT Loading Loading @@ -363,6 +364,8 @@ def get_validation_errors(outfile, app=None): for it in opts.index_together: validate_local_fields(e, opts, "index_together", it) validate_model_signals(e) return len(e.errors) Loading @@ -382,3 +385,28 @@ def validate_local_fields(e, opts, field_name, fields): e.add(opts, '"%s" refers to %s. ManyToManyFields are not supported in %s.' % (field_name, f.name, field_name)) if f not in opts.local_fields: e.add(opts, '"%s" refers to %s. This is not in the same model as the %s statement.' % (field_name, f.name, field_name)) def validate_model_signals(e): """Ensure lazily referenced model signals senders are installed.""" from django.db import models for name in dir(models.signals): obj = getattr(models.signals, name) if isinstance(obj, models.signals.ModelSignal): for reference, receivers in obj.unresolved_references.items(): for receiver, _, _ in receivers: # The receiver is either a function or an instance of class # defining a `__call__` method. if isinstance(receiver, types.FunctionType): description = "The `%s` function" % receiver.__name__ else: description = "An instance of the `%s` class" % receiver.__class__.__name__ e.add( receiver.__module__, "%s was connected to the `%s` signal " "with a lazy reference to the '%s' sender, " "which has not been installed." % ( description, name, '.'.join(reference) ) ) django/db/models/signals.py +59 −9 Original line number Diff line number Diff line from collections import defaultdict from django.db.models.loading import get_model from django.dispatch import Signal from django.utils import six class_prepared = Signal(providing_args=["class"]) pre_init = Signal(providing_args=["instance", "args", "kwargs"], use_caching=True) post_init = Signal(providing_args=["instance"], use_caching=True) pre_save = Signal(providing_args=["instance", "raw", "using", "update_fields"], class ModelSignal(Signal): """ Signal subclass that allows the sender to be lazily specified as a string of the `app_label.ModelName` form. """ def __init__(self, *args, **kwargs): super(ModelSignal, self).__init__(*args, **kwargs) self.unresolved_references = defaultdict(list) class_prepared.connect(self._resolve_references) def _resolve_references(self, sender, **kwargs): opts = sender._meta reference = (opts.app_label, opts.object_name) try: receivers = self.unresolved_references.pop(reference) except KeyError: pass else: for receiver, weak, dispatch_uid in receivers: super(ModelSignal, self).connect( receiver, sender=sender, weak=weak, dispatch_uid=dispatch_uid ) def connect(self, receiver, sender=None, weak=True, dispatch_uid=None): if isinstance(sender, six.string_types): try: app_label, object_name = sender.split('.') except ValueError: raise ValueError( "Specified sender must either be a model or a " "model name of the 'app_label.ModelName' form." ) sender = get_model(app_label, object_name, only_installed=False) if sender is None: reference = (app_label, object_name) self.unresolved_references[reference].append( (receiver, weak, dispatch_uid) ) return super(ModelSignal, self).connect( receiver, sender=sender, weak=weak, dispatch_uid=dispatch_uid ) pre_init = ModelSignal(providing_args=["instance", "args", "kwargs"], use_caching=True) post_init = ModelSignal(providing_args=["instance"], use_caching=True) pre_save = ModelSignal(providing_args=["instance", "raw", "using", "update_fields"], use_caching=True) post_save = Signal(providing_args=["instance", "raw", "created", "using", "update_fields"], use_caching=True) post_save = ModelSignal(providing_args=["instance", "raw", "created", "using", "update_fields"], use_caching=True) pre_delete = Signal(providing_args=["instance", "using"], use_caching=True) post_delete = Signal(providing_args=["instance", "using"], use_caching=True) pre_delete = ModelSignal(providing_args=["instance", "using"], use_caching=True) post_delete = ModelSignal(providing_args=["instance", "using"], use_caching=True) m2m_changed = ModelSignal(providing_args=["action", "instance", "reverse", "model", "pk_set", "using"], use_caching=True) pre_migrate = Signal(providing_args=["app", "create_models", "verbosity", "interactive", "db"]) pre_syncdb = pre_migrate post_migrate = Signal(providing_args=["class", "app", "created_models", "verbosity", "interactive", "db"]) post_syncdb = post_migrate m2m_changed = Signal(providing_args=["action", "instance", "reverse", "model", "pk_set", "using"], use_caching=True) docs/ref/signals.txt +9 −1 Original line number Diff line number Diff line Loading @@ -22,7 +22,7 @@ Model signals :synopsis: Signals sent by the model system. The :mod:`django.db.models.signals` module defines a set of signals sent by the module system. model system. .. warning:: Loading @@ -37,6 +37,14 @@ module system. so if your handler is a local function, it may be garbage collected. To prevent this, pass ``weak=False`` when you call the signal's :meth:`~django.dispatch.Signal.connect`. .. versionadded:: 1.7 Model signals ``sender`` model can be lazily referenced when connecting a receiver by specifying its full application label. For example, an ``Answer`` model defined in the ``polls`` application could be referenced as ``'polls.Answer'``. This sort of reference can be quite handy when dealing with circular import dependencies and swappable models. pre_init -------- Loading docs/releases/1.7.txt +5 −1 Original line number Diff line number Diff line Loading @@ -425,7 +425,7 @@ Models * Is it now possible to avoid creating a backward relation for :class:`~django.db.models.OneToOneField` by setting its :attr:`~django.db.models.ForeignKey.related_name` to `'+'` or ending it with `'+'`. ``'+'`` or ending it with ``'+'``. * :class:`F expressions <django.db.models.F>` support the power operator (``**``). Loading @@ -436,6 +436,10 @@ Signals * The ``enter`` argument was added to the :data:`~django.test.signals.setting_changed` signal. * The model signals can be now be connected to using a ``str`` of the ``'app_label.ModelName'`` form – just like related fields – to lazily reference their senders. Templates ^^^^^^^^^ Loading docs/topics/auth/customizing.txt +13 −0 Original line number Diff line number Diff line Loading @@ -413,6 +413,19 @@ different User model. class Article(models.Model): author = models.ForeignKey(settings.AUTH_USER_MODEL) .. versionadded:: 1.7 When connecting to signals sent by the User model, you should specify the custom model using the :setting:`AUTH_USER_MODEL` setting. For example:: from django.conf import settings from django.db.models.signals import post_save def post_save_receiver(signal, sender, instance, **kwargs): pass post_save.connect(post_save_receiver, sender=settings.AUTH_USER_MODEL) Specifying a custom User model ------------------------------ Loading Loading
django/core/management/validation.py +29 −1 Original line number Diff line number Diff line import collections import sys import types from django.conf import settings from django.core.management.color import color_style Loading @@ -25,7 +26,7 @@ def get_validation_errors(outfile, app=None): validates all models of all installed apps. Writes errors, if any, to outfile. Returns number of errors. """ from django.db import models, connection from django.db import connection, models from django.db.models.loading import get_app_errors from django.db.models.deletion import SET_NULL, SET_DEFAULT Loading Loading @@ -363,6 +364,8 @@ def get_validation_errors(outfile, app=None): for it in opts.index_together: validate_local_fields(e, opts, "index_together", it) validate_model_signals(e) return len(e.errors) Loading @@ -382,3 +385,28 @@ def validate_local_fields(e, opts, field_name, fields): e.add(opts, '"%s" refers to %s. ManyToManyFields are not supported in %s.' % (field_name, f.name, field_name)) if f not in opts.local_fields: e.add(opts, '"%s" refers to %s. This is not in the same model as the %s statement.' % (field_name, f.name, field_name)) def validate_model_signals(e): """Ensure lazily referenced model signals senders are installed.""" from django.db import models for name in dir(models.signals): obj = getattr(models.signals, name) if isinstance(obj, models.signals.ModelSignal): for reference, receivers in obj.unresolved_references.items(): for receiver, _, _ in receivers: # The receiver is either a function or an instance of class # defining a `__call__` method. if isinstance(receiver, types.FunctionType): description = "The `%s` function" % receiver.__name__ else: description = "An instance of the `%s` class" % receiver.__class__.__name__ e.add( receiver.__module__, "%s was connected to the `%s` signal " "with a lazy reference to the '%s' sender, " "which has not been installed." % ( description, name, '.'.join(reference) ) )
django/db/models/signals.py +59 −9 Original line number Diff line number Diff line from collections import defaultdict from django.db.models.loading import get_model from django.dispatch import Signal from django.utils import six class_prepared = Signal(providing_args=["class"]) pre_init = Signal(providing_args=["instance", "args", "kwargs"], use_caching=True) post_init = Signal(providing_args=["instance"], use_caching=True) pre_save = Signal(providing_args=["instance", "raw", "using", "update_fields"], class ModelSignal(Signal): """ Signal subclass that allows the sender to be lazily specified as a string of the `app_label.ModelName` form. """ def __init__(self, *args, **kwargs): super(ModelSignal, self).__init__(*args, **kwargs) self.unresolved_references = defaultdict(list) class_prepared.connect(self._resolve_references) def _resolve_references(self, sender, **kwargs): opts = sender._meta reference = (opts.app_label, opts.object_name) try: receivers = self.unresolved_references.pop(reference) except KeyError: pass else: for receiver, weak, dispatch_uid in receivers: super(ModelSignal, self).connect( receiver, sender=sender, weak=weak, dispatch_uid=dispatch_uid ) def connect(self, receiver, sender=None, weak=True, dispatch_uid=None): if isinstance(sender, six.string_types): try: app_label, object_name = sender.split('.') except ValueError: raise ValueError( "Specified sender must either be a model or a " "model name of the 'app_label.ModelName' form." ) sender = get_model(app_label, object_name, only_installed=False) if sender is None: reference = (app_label, object_name) self.unresolved_references[reference].append( (receiver, weak, dispatch_uid) ) return super(ModelSignal, self).connect( receiver, sender=sender, weak=weak, dispatch_uid=dispatch_uid ) pre_init = ModelSignal(providing_args=["instance", "args", "kwargs"], use_caching=True) post_init = ModelSignal(providing_args=["instance"], use_caching=True) pre_save = ModelSignal(providing_args=["instance", "raw", "using", "update_fields"], use_caching=True) post_save = Signal(providing_args=["instance", "raw", "created", "using", "update_fields"], use_caching=True) post_save = ModelSignal(providing_args=["instance", "raw", "created", "using", "update_fields"], use_caching=True) pre_delete = Signal(providing_args=["instance", "using"], use_caching=True) post_delete = Signal(providing_args=["instance", "using"], use_caching=True) pre_delete = ModelSignal(providing_args=["instance", "using"], use_caching=True) post_delete = ModelSignal(providing_args=["instance", "using"], use_caching=True) m2m_changed = ModelSignal(providing_args=["action", "instance", "reverse", "model", "pk_set", "using"], use_caching=True) pre_migrate = Signal(providing_args=["app", "create_models", "verbosity", "interactive", "db"]) pre_syncdb = pre_migrate post_migrate = Signal(providing_args=["class", "app", "created_models", "verbosity", "interactive", "db"]) post_syncdb = post_migrate m2m_changed = Signal(providing_args=["action", "instance", "reverse", "model", "pk_set", "using"], use_caching=True)
docs/ref/signals.txt +9 −1 Original line number Diff line number Diff line Loading @@ -22,7 +22,7 @@ Model signals :synopsis: Signals sent by the model system. The :mod:`django.db.models.signals` module defines a set of signals sent by the module system. model system. .. warning:: Loading @@ -37,6 +37,14 @@ module system. so if your handler is a local function, it may be garbage collected. To prevent this, pass ``weak=False`` when you call the signal's :meth:`~django.dispatch.Signal.connect`. .. versionadded:: 1.7 Model signals ``sender`` model can be lazily referenced when connecting a receiver by specifying its full application label. For example, an ``Answer`` model defined in the ``polls`` application could be referenced as ``'polls.Answer'``. This sort of reference can be quite handy when dealing with circular import dependencies and swappable models. pre_init -------- Loading
docs/releases/1.7.txt +5 −1 Original line number Diff line number Diff line Loading @@ -425,7 +425,7 @@ Models * Is it now possible to avoid creating a backward relation for :class:`~django.db.models.OneToOneField` by setting its :attr:`~django.db.models.ForeignKey.related_name` to `'+'` or ending it with `'+'`. ``'+'`` or ending it with ``'+'``. * :class:`F expressions <django.db.models.F>` support the power operator (``**``). Loading @@ -436,6 +436,10 @@ Signals * The ``enter`` argument was added to the :data:`~django.test.signals.setting_changed` signal. * The model signals can be now be connected to using a ``str`` of the ``'app_label.ModelName'`` form – just like related fields – to lazily reference their senders. Templates ^^^^^^^^^ Loading
docs/topics/auth/customizing.txt +13 −0 Original line number Diff line number Diff line Loading @@ -413,6 +413,19 @@ different User model. class Article(models.Model): author = models.ForeignKey(settings.AUTH_USER_MODEL) .. versionadded:: 1.7 When connecting to signals sent by the User model, you should specify the custom model using the :setting:`AUTH_USER_MODEL` setting. For example:: from django.conf import settings from django.db.models.signals import post_save def post_save_receiver(signal, sender, instance, **kwargs): pass post_save.connect(post_save_receiver, sender=settings.AUTH_USER_MODEL) Specifying a custom User model ------------------------------ Loading