Detailed description of Signal code in Django and djangosignal

Source: Internet
Author: User

Detailed description of Signal code in Django and djangosignal

This article focuses on the relevant content of signal in Django development. The details are as follows.

Preface

In web development, you may encounter the following scenarios:

After a user completes an operation, the user automatically performs subsequent operations. For example, after the user completes the password change,
You need to send a confirmation email.

Of course, you can write the logic together, but there is a problem that there are generally more than one trigger operation (for example, the user has changed the confirmation email for other information). At this time, this logic will need to be written multiple times, so you may think about DRY (Don't repeat yourself), So you write it into a function and call it every time. Of course this is okay.

However, if you change your mind, you will find another completely different solution, namely:

  • Programs similar to daemon listen to specific events
  • Pre-operation to trigger the corresponding event
  • The listener executes the corresponding operation.

What are the benefits?

  • Loose coupling (do not write subsequent operations in the main logic)
  • It is easy to reuse (this is why django itself and third-party applications such as pinax use this technology in large quantities). Similar features such as java and javascript are available in various advanced languages, in django, we use signal.
Observer Mode

Siganl is a "Signal distributor" provided in the Django framework ". It is an implementation application of the observer pattern that is often mentioned in the design pattern.

In this mode, a target object manages all observer objects that depend on it and actively sends notifications when its status changes. This is usually done by calling the methods provided by various observers.

Scenarios of observer Mode

  • In Association Behavior scenarios, you must note that association behavior can be split rather than "combined.
  • Multi-level event triggering scenarios.
  • Cross-system message exchange scenarios, such as message queues and event bus processing mechanisms.

Advantages

1. Remove coupling, so that both sides of coupling depend on abstraction, so that their transformations do not affect the transformation on the other side.

It establishes an abstract coupling between the observer and the observer. What the observer role knows is a specific observer list. Each specific observer conforms to an abstract observer interface. The observer does not know any specific observer, but only knows that they all have a common interface.

Since the observer and observer are not closely coupled, they can belong to different abstract layers. This coupling greatly improves code readability and maintainability.

2. The observer mode achieves dynamic interaction;

Because the observer mode manages the observer registration, you can dynamically control the interaction scope of an action by dynamically controlling the registered observer during the operation to achieve dynamic interaction.

3. The observer mode supports broadcast communication.

The notification sent by the target to the observer is intended for all registered observers. Therefore, the information of each target notification must be broadcast to all registered observers, you can also add a new method on the target to limit the broadcast range.

A typical application of the Siganl mechanism in Django is that the framework creates signals associated with some method calls of the Model, such as pre_save and post_save, for Models, for example, pre_save and post_save notify the observer before and after calling the save () method of Modle, so that the observer can perform a series of operations.

The processing of django signal is synchronous. Do not use it to process large batches of tasks.
Django signal is very helpful for program decoupling, code reuse, and maintainability.

Implementation of the Signal Mechanism

The source code of Siganl is located under the django dispatch package, and the main code is located in dispatcher. py.

The Signal class is defined in dispatcher, and a method for connecting signals and signal receivers using the Python modifier (Signal, ** kwargs ).

Class Signal (object): "Base class for all signals Internal attributes: receivers {receiverkey (id): weakref (receiver )} "def _ init _ (self, providing_args = None, use_caching = False):" "Create a new Signal providing_args parameter, specify the information parameters that the Siganl can provide to the observer when sending an event (calling the send method). For example, post_save () carries the corresponding instance object and update_fields "self. receivers = [] if providing_args is None: providing_args = [] self. providing_args = set (providing_args) self. lock = threading. lock () 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 = weakref. weakKeyDictionary () if use_caching else {} self. _ dead_receivers = False def connect (self, Cycler, sender = None, weak = True, dispatch_uid = None): from django. conf import settings if dispatch_uid: lookup_key = (dispatch_uid, _ make_id (sender) else: lookup_key = (_ make_id (worker ER), _ make_id (sender) if weak: ref = weakref. ref receiver_object = receiver # Check for bound methods # construct weak referenced referers if hasattr (receiver, '_ self _') and hasattr (receiver, '_ func _'): ref = WeakMethod receiver_object = explorer. _ self _ if sys. version_info> = (3, 4): worker ER = ref (worker ER) weakref. finalize (receiver_object, self. _ remove_receiver) else: Explorer = ref (explorer, self. _ remove_receiver) with self. lock: # clear receivers self. _ clear_dead_receivers () for r_key, _ in self. receivers: if r_key = lookup_key: break else: self. receivers. append (lookup_key, receiver) self. sender_receivers_cache.clear () def disconnect (self, Cycler = None, sender = None, weak = True, dispatch_uid = None): if dispatch_uid: lookup_key = (dispatch_uid, _ make_id (sender )) else: lookup_key = (_ make_id (Explorer), _ make_id (sender) disconnected = False with self. lock: self. _ clear_dead_receivers () for index in range (len (self. receivers): (r_key, _) = self. receivers [index] if r_key = lookup_key: disconnected = True del self. receivers [index] break self. sender_receivers_cache.clear () return disconnected def has_listeners (self, sender = None): return bool (self. _ live_receivers (sender) def send (self, sender, ** named): responses = [] if not self. receivers or self. sender_receivers_cache.get (sender) is NO_RECEIVERS: return responses for each er in self. _ live_receivers (sender): response = receiver Er (signal = self, sender = sender, ** named) responses. append (handler er, response) return responses def send_robust (self, sender, ** named): responses = [] if not self. receivers or self. sender_receivers_cache.get (sender) is NO_RECEIVERS: return responses # Call each aggreger with whatever arguments it can accept. # Return a list of tuple pairs [(receiver, response),...]. for each er in self. _ live_receivers (sender): try: response = Cycler (signal = self, sender = sender, ** named) handle T Exception as err: if not hasattr (err, '_ traceback _'): err. _ traceback _ = sys. exc_info () [2] responses. append (assumer, err) else: responses. append (handler er, response) return responses def _ clear_dead_receivers (self): # Note: caller is assumed to hold self. lock. if self. _ dead_receivers: self. _ dead_receivers = False new_receivers = [] for r in self. receivers: if isinstance (r [1], weakref. referenceType) and r [1] () is None: continue new_receivers.append (r) self. receivers = new_receivers def _ live_receivers (self, sender): "filter out Garbage Collector" receivers = None # If cache is used, if the _ remove_receiver function is not called, search for if self in sender_receivers_cache. use_caching and not self. _ dead_receivers: receivers = self. sender_receivers_cache.get (sender) # We cocould end up here with NO_RECEIVERS even if we do check this case in #. send () prior to calling _ live_receivers () due to concurrent. send () call. if receivers is NO_RECEIVERS: return [] if receivers is None: with self. lock: self. _ clear_dead_receivers () senderkey = _ make_id (sender) receivers = [] for (receiverkey, r_senderkey), aggreger in self. receivers: if r_senderkey = NONE_ID or r_senderkey = senderkey: receivers. append (Consumer ER) if self. use_caching: if not receivers: self. sender_receivers_cache [sender] = NO_RECEIVERS else: # Note, we must cache the weakref versions. self. sender_receivers_cache [sender] = receivers non_weak_receivers = [] for receiver er in receivers: if isinstance (receiver er, weakref. referenceType): # Dereference the weak reference. extends ER = receiver () if your ER is not None: non_weak_receivers.append (receiver ER) else: Terminate (receiver ER) return non_weak_receivers def _ remove_receiver (self, receiver ER = None): self. _ dead_receivers = True

Connect Method

The connect method is used to connect signals and signal processing functions. A similar concept is equivalent to registering an observer (processing function) for an event (indicating an event when a signal is sent ), in the function parameter, the handler is the signal processing function (the function is also an object, which is too convenient). The sender indicates the signal sender, such as the post_save () signal in the Django framework, any model sends this signal after the save () function is called, but we only want to pay attention to the event of the save () method call of a model, you can specify sender as the model class that we need to pay attention.

The weak parameter indicates whether to convert a consumer to a weak reference object. In Siganl, all referers are converted to weak references by default. Therefore, if your consumer is a local object, then the consumer er may be reclaimed by the garbage collection period, and the consumer er will become a dead_consumer er. The Siganl will clear the dead_consumer er when the connect and disconnect methods are called.

dispatch_uidThis parameter is used to uniquely identify the aggreger function. The main function is to prevent the aggreger function from being registered multiple times. This will cause the aggreger function to be executed multiple times, this may be a result we don't want.

Disconnect Method

The disconnect method is used to disconnect the receiver. The function first generates an identifier based on the sender and receiver objects.lookup_keyWhen traversing the parser array, find the receiver to be disconnect Based on lookup_key and then delete the receiver from the array.

Send and send_robust

Both the send and send_robust methods are used to send events. The difference is that the send_robust function captures exceptions from the signal receiving function and adds them to the returned responses array.

Use of the Siganl class

Shows the process of Django signal:

Use of built-in signal

Model-related:

  • Pre_save object triggered before saving
  • Post_save object is triggered after saving
  • Pre_delete object triggered before delete
  • Triggered after the post_delete object is deleted
  • Triggered after m2m_changed ManyToManyField is updated

Request related:

  • Request_started triggered before a request
  • Triggered after request_finished request

For the built-in signal of django, we only need to write the handler, as follows.

Step 1: Write the handler and bind it to signal

# Myapp/signals/handlers. pyfrom django. dispatch import receiverfrom django. core. signals import request_finished # decorators binding @ author Er (request_finished, dispatch_uid = "request_finished") def handler (sender, ** kwargs): print ("Request finished! ===================================== ") # Normal binding mode def my_signal_handler (sender, ** kwargs): print ("Request finished! ================================ ") Request_finished.connect (my_signal_handler) ######################################## ############# model-specific signal from django. dispatch import receiverfrom django. db. models. signals import post_savefrom polls. models import MyModel @ author Er (post_save, sender = MyModel, dispatch_uid = "mymodel_post_save") def my_model_handler (sender, ** kwargs): print ('saved :{}'. format (kwargs ['instance']. _ dict __))

Usedispatch_uidMake sure that the worker calls only once.

Step 2: load signal

# myapp/__init__pydefault_app_config = 'myapp.apps.MySendingAppConfig'
# myapp/apps.pyfrom django.apps import AppConfigclass MyAppConfig(AppConfig):  name = 'myapp'  def ready(self):    # signals are imported, so that they are defined and can be used    import myapp.signals.handlers

At this point, when the system receives a request, it will execute the handler.

For other built-in signal, refer to the official documentation:

Https://docs.djangoproject.com/en/1.9/topics/signals/

Use of custom signal

To customize signal, we need to write signal and receiver.

Step 1: Compile signal

myapp.signals.signals.pyimportdjango.dispatchmy_signal = django.dispatch.Signal(providing_args=["my_signal_arg1", "my_signal_arg_2"])

Step 2: load signal

# myapp/__init__pydefault_app_config = 'myapp.apps.MySendingAppConfig'myapp/apps.pyfrom django.apps import AppConfigclass MyAppConfig(AppConfig):  name = 'myapp'  def ready(self):    # signals are imported, so that they are defined and can be used    import myapp.signals.handlers

Step 3: Send signal when an event is triggered

# myapp/views.pyfrom .signals.signals import my_signalmy_signal.send(sender="some function or class",        my_signal_arg1="something", my_signal_arg_2="something else"])

Custom signal, django has compiled event listening for us here.

Step 4: receive the signal and execute the worker er

# myapp/signals/handlers.pyfrom django.dispatch import receiverfrom myapp.signals.signals import my_signal@receiver(my_signal, dispatch_uid="my_signal_receiver")def my_signal_handler(sender, **kwargs):  print('my_signal received')

Now, the custom signal is developed.

Summary

The above is all the details about the Signal code in Django. I hope it will be helpful to you. If you are interested, you can continue to refer to other related topics on this site. If you have any shortcomings, please leave a message. Thank you for your support!

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.