Django中的Signal代码详解
本文研究的主要是Django开发中的signal 的相关内容,具体如下。 前言在web开发中, 你可能会遇到下面这种场景:
当然可以把逻辑写在一起,但是有个问题是,触发操作一般不止一种(如用户更改了其它信息的确认邮件),这时候这个逻辑会需要写多次,所以你可能会想着DRY(Don't repeat yourself),于是你把它写到了一个函数中,每次调用。当然这是没问题的. 但是, 如果你换个思路你会发现另一个完全不同的方案, 即:
这样的好处是什么呢?
观察者模式Siganl是Django框架中提供的一个 “信号分发器”。它是设计模式中经常提到的观察者模式的一个实现应用。 在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。 观察者模式的使用场景
优点 1.解除耦合,让耦合的双方都依赖于抽象,从而使得各自的变换都不会影响另一边的变换。 它在被观察者和观察者之间建立一个抽象的耦合。被观察者角色所知道的只是一个具体观察者列表,每一个具体观察者都符合一个抽象观察者的接口。被观察者并不认识任何一个具体观察者,它只知道它们都有一个共同的接口。 由于被观察者和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。这种耦合性使得代码的可读性、维护性大大提高。 2.观察者模式实现了动态联动; 由于观察者模式对观察者注册实行管理,那就可以在运行期间,通过动态的控制注册的观察者来控制某个动作的联动范围,从而实现动态联动。 3.观察者模式支持广播通信。 目标发送通知给观察者是面向所有注册的观察者,所以目标每次通知的信息就要对所有注册的观察者进行广播,也可以在目标上添加新的方法来限制广播的范围。 Django 中Siganl 机制的典型应用是,框架为 Models 创建了 pre_save、post_save等与Model的某些方法调用相关联的信号,如pre_save 和 post_save 分别会在 Modle的save()方法的调用之前和之后通知观察者,从而让观察者进行一系列操作。 django signal的处理是同步的,勿用于处理大批量任务。 Signal 机制的实现方式Siganl的源码位于django dispatch包下,主要的代码位于 dispatcher.py中。 在dispatcher中定义了Signal类,以及一个用于使用Python装饰器的方式来连接信号以及信号接受者的方法receiver(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): """ 创建一个新的Signal providing_args 参数,指定这个Siganl 在发出事件(调用send方法)时,可以提供给观察者的信息参数 比如 post_save()会带上 对应的instance对象,以及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,receiver,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(receiver),_make_id(sender)) if weak: ref = weakref.ref receiver_object = receiver # Check for bound methods # 构造弱引用的的receiver if hasattr(receiver,'__self__') and hasattr(receiver,'__func__'): ref = WeakMethod receiver_object = receiver.__self__ if sys.version_info >= (3,4): receiver = ref(receiver) weakref.finalize(receiver_object,self._remove_receiver) else: receiver = ref(receiver,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,receiver=None,dispatch_uid=None): if dispatch_uid: lookup_key = (dispatch_uid,_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 receiver in self._live_receivers(sender): response = receiver(signal=self,sender=sender,**named) responses.append((receiver,response)) return responses def send_robust(self,**named): responses = [] if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS: return responses # Call each receiver with whatever arguments it can accept. # Return a list of tuple pairs [(receiver,response),... ]. for receiver in self._live_receivers(sender): try: response = receiver(signal=self,**named) except Exception as err: if not hasattr(err,'__traceback__'): err.__traceback__ = sys.exc_info()[2] responses.append((receiver,err)) else: responses.append((receiver,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): """ 过滤掉 已经被 垃圾回收的receiver """ receivers = None # 如果使用了cache,并且没有调用过_remove_receiver 函数 则去 sender_receivers_cache中查找 if self.use_caching and not self._dead_receivers: receivers = self.sender_receivers_cache.get(sender) # We could 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),receiver in self.receivers: if r_senderkey == NONE_ID or r_senderkey == senderkey: receivers.append(receiver) 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 in receivers: if isinstance(receiver,weakref.ReferenceType): # Dereference the weak reference. receiver = receiver() if receiver is not None: non_weak_receivers.append(receiver) else: non_weak_receivers.append(receiver) return non_weak_receivers def _remove_receiver(self,receiver=None): self._dead_receivers = True connect方法 connect方法用于连接信号和信号处理函数,类似的概念相当于为某个事件(信号发出表示一个事件)注册观察者(处理函数),函数参数中receiver就是信号处理函数(函数也是对象,这太方便了),sender表示信号的发送者,比如Django框架中的post_save()这个信号,任何一个模型在save()函数调用之后都会发出这个信号,但是我们只想关注某一个模型 save()方法调用的事件发生,就可以指定sender为我们需要关注的模型类。 weak参数表示是否将receiver转换成弱引用对象,Siganl中默认会将所有的receiver转成弱引用,所以如果你的receiver是个局部对象的话,那么receiver可能会被垃圾回收期回收,receiver也就变成一个dead_receiver了,Siganl会在connect和disconnect方法调用的时候,清除dead_receiver。
disconnect方法 disconnect方法用于断开信号的接收器,函数内首先会生成根据sender和receiver对象构造出的一个标识 send和send_robust send和send_robust方法都是用于发送事件的函数,不同点在于send_robust函数中会捕获信号接收函数发生的异常,添加到返回的responses数组中。 Siganl类的使用Django signal的处理过程如下图所示: 内建signal的使用模型相关:
请求相关:
针对django自带的signal,我们只需要编写receiver 即可,使用如下。 第一步,编写receiver并绑定到signal # myapp/signals/handlers.py from django.dispatch import receiver from django.core.signals import request_finished ## decorators 方式绑定 @receiver(request_finished,dispatch_uid="request_finished") def my_signal_handler(sender,**kwargs): print("Request finished!================================") # 普通绑定方式 def my_signal_handler(sender,**kwargs): print("Request finished!================================") request_finished.connect(my_signal_handler) ##################################################### # 针对model 的signal from django.dispatch import receiver from django.db.models.signals import post_save from polls.models import MyModel @receiver(post_save,sender=MyModel,dispatch_uid="mymodel_post_save") def my_model_handler(sender,**kwargs): print('Saved: {}'.format(kwargs['instance'].__dict__)) 用 第二步,加载signal # myapp/__init__py default_app_config = 'myapp.apps.MySendingAppConfig' # myapp/apps.py from django.apps import AppConfig class MyAppConfig(AppConfig): name = 'myapp' def ready(self): # signals are imported,so that they are defined and can be used import myapp.signals.handlers 到此,当系统受到request 请求完成后,便会执行receiver。 其他内建的signal,参考官方文档: https://docs.djangoproject.com/en/1.9/topics/signals/ 自定义signal的使用自定义signal,需要我们编写signal和receiver。 第一步,编写signal myapp.signals.signals.py importdjango.dispatch my_signal = django.dispatch.Signal(providing_args=["my_signal_arg1","my_signal_arg_2"]) 第二步,加载signal # myapp/__init__py default_app_config = 'myapp.apps.MySendingAppConfig' myapp/apps.py from django.apps import AppConfig class MyAppConfig(AppConfig): name = 'myapp' def ready(self): # signals are imported,so that they are defined and can be used import myapp.signals.handlers 第三步,事件触发时,发送signal # myapp/views.py from .signals.signals import my_signal my_signal.send(sender="some function or class",my_signal_arg1="something",my_signal_arg_2="something else"]) 自定义的signal,django已经为我们编写了此处的事件监听。 第四步,收到signal,执行receiver # myapp/signals/handlers.py from django.dispatch import receiver from myapp.signals.signals import my_signal @receiver(my_signal,dispatch_uid="my_signal_receiver") def my_signal_handler(sender,**kwargs): print('my_signal received') 此时,我们自定义的signal 便开发完成了。 总结以上就是本文关于Django中的Signal代码详解的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持! 您可能感兴趣的文章:
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- python – 自定义权重初始化tensorflow tf.layers.dense
- python – 神秘的GObject警告:断言`G_IS_OBJECT(object)’
- Django中利用filter与simple_tag为前端自定义函数的实现方法
- 在Google App Engine上使用Python进行开发时,我应该使用哪个
- tkinter学习系列(三)之Label控件
- 开始新月时的Python日期问题
- 为什么一个版本泄漏内存但不泄漏另一个版本? (Python)
- Python究竟有多强?只需十行代码就能把微信玩“坏”!微信轰
- OpenCV Python,为什么Hough Circle Transform找不到所有圆圈
- Open CV非常牛逼!众所周知!今天就来见识一下它究竟有多牛