Defining Signals
Django itself provides some common signals, and users can define their own signals as well
Defining a signal is simple, just instantiate a Signal instance
When instantiating a Signal, you can pass in the keyword argument providing_args, providing_args is a list that defines the arguments that can be passed in when the current signal calls the send method.
# from import Signal request_started = Signal(providing_args=["environ"]) request_finished = Signal() got_request_exception = Signal(providing_args=["request"]) setting_changed = Signal(providing_args=["setting", "value", "enter"])
where the initialization of Signal is also relatively simple, by defining a thread lock for the instantiated signal
class Signal(object): def __init__(self, providing_args=None, use_caching=False): = [] if providing_args is None: providing_args = [] self.providing_args = set(providing_args) = () self.use_caching = use_caching # For convenience we create empty caches even if they are not used. # A note about caching: if use_caching is defined, then for each # distinct sender we cache the receivers that sender has in # 'sender_receivers_cache'. The cache is cleaned when .connect() or # .disconnect() is called and populated on send(). self.sender_receivers_cache = () if use_caching else {} self._dead_receivers = False
Defining Signal Handler Functions
A Signal handler is a function or a method of an instance and must satisfy the following conditions:
- hashable
- Can receive keyword parameters
There are two keyword arguments that must be included in one of the handler functions:
- signal, the Signal instance to be received
- sender, the trigger of the Signal to be received
# .__init__.py from import signals from import ConnectionHandler connections = ConnectionHandler() def reset_queries(**kwargs): for conn in (): conn.queries_log.clear() signals.request_started.connect(reset_queries) def close_old_connections(**kwargs): for conn in (): conn.close_if_unusable_or_obsolete() signals.request_started.connect(close_old_connections) signals.request_finished.connect(close_old_connections)
Handler Binding Signal
django provides two ways to bind Signal handlers to Signal instances:
- Calling the connect method manually
- Using the decorator receiver
Actually, the decorator receiver ends up calling the connect method to bind the handler function to the Signal instance
The connect method of the Signal class is defined as follows:
class Signal(object): ... def connect(self, receiver, sender=None, weak=True, dispatch_uid=None): from import settings # If DEBUG is on, check that we got a good receiver if and : assert callable(receiver), "Signal receivers must be callable." # Check for **kwargs if not func_accepts_kwargs(receiver): raise ValueError("Signal receivers must accept keyword arguments (**kwargs).") if dispatch_uid: lookup_key = (dispatch_uid, _make_id(sender)) else: lookup_key = (_make_id(receiver), _make_id(sender)) if weak: ref = receiver_object = receiver # Check for bound methods if hasattr(receiver, '__self__') and hasattr(receiver, '__func__'): ref = WeakMethod receiver_object = receiver.__self__ if six.PY3: receiver = ref(receiver) (receiver_object, self._remove_receiver) else: receiver = ref(receiver, self._remove_receiver) with : self._clear_dead_receivers() for r_key, _ in : if r_key == lookup_key: break else: ((lookup_key, receiver)) self.sender_receivers_cache.clear()
Each signal can get a lookup_key that uniquely identifies a Signal and its handler, when calling the connect method of a Signal instance, it will determine if the bound handler is already in its own receivers, and if it is, it won't re-register.
Send Singal
With the Signal instance defined earlier, and the Signal instance handler defined, a Signal instance bound by a handler function can be used to send signals where necessary, and then be handled by the bound handler function.
# from threading import Lock from import signals from import base class WSGIHandler(): ... def __call__(self, environ, start_response): ... signals.request_started.send(sender=self.__class__, environ=environ) ...
Signal most Django a core knowledge in the project rarely used, so many people do not understand or have not heard of (including me). Simply put, before and after some operations we can issue a signal to get a specific operation, these operations include
.pre_save&.post_save
Sent before or after the model save() method is called.
.pre_delete&.post_delete
Sent before or after the model delete() method or the delete() method call for a query set.
.m2m_changed
Sent on ManyToManyField modifications on the model.
.request_started&.request_finished
Sent when Django starts or completes an HTTP request.
For other detailed points, you can click on the link to see them explained directly through an example:
In the custom user model class, in the background to add user data because of the use of custom model class create so the password will be saved in clear text, the next use of semaphores in the saved immediately after the password to change the solution. (An example of an online project)
users/
from import post_save from import receiver from import get_user_model User = get_user_model() # post_save: one of the seven methods above: operations after the model has been saved # sender: the mod that sends the signal @receiver(post_save, sender=User) def create_user(sender, instance=None, created=False, **kwargs): """ sender:model class。 instance:Practical examples of preservation。 created:If a new record is createdTrue。 update_fields:()The set of fields to be updated,If not passed then theNone """ if created: password = # instance is equivalent to user instance.set_password(password) ()
users/
from import AppConfig class UsersConfig(AppConfig): name = 'users' verbose_name = 'User Management' def ready(self): """Use ready to load, otherwise it won't work.""" import
This is the whole content of this article.