在django中有1个比较好玩的技术。那就是信号的绑定和接受技术。
从项目开发的角度,django中的信号处理技术属于辅助功能。流程并不清晰。
django提供的信号有
class_prepared = Signal(providing_args=["class"]) pre_init = Signal(providing_args=["instance", "args", "kwargs"]) post_init = Signal(providing_args=["instance"]) pre_save = Signal(providing_args=["instance", "raw", "using", "update_fields"]) post_save = Signal(providing_args=["instance", "raw", "created", "using", "update_fields"]) pre_delete = Signal(providing_args=["instance", "using"]) post_delete = Signal(providing_args=["instance", "using"]) post_syncdb = Signal(providing_args=["class", "app", "created_models", "verbosity", "interactive"]) m2m_changed = Signal(providing_args=["action", "instance", "reverse", "model", "pk_set", "using"])
request_started = Signal() request_finished = Signal() got_request_exception = Signal(providing_args=["request"])
在信号内部主要通过receiver和sender(允许为None)的key组合来保存信号槽。
当send发送信号时候。通过遍历信号槽列表。判断sender是否允许。如果允许,则调用函数。
有1点可以确定的是,信号使用的是单件方式。也就是说要全局唯一性。使用太多对性能是一种负担。因此最好只实现Django自带的就可以了。
下面是抽出来的演示代码
from django.dispatch import saferef import weakref def _make_id(target): if hasattr(target, '__func__'): return (id(target.__self__), id(target.__func__)) return id(target) import threading WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref) class Signal(object): def __init__(self): self.receivers = [] self.lock = threading.Lock() def connect(self, receiver, sender=None, weak=True, dispatch_uid=None): import inspect assert callable(receiver) , "Signal receivers must be callable." try: argspec = inspect.getargspec(receiver.__call__) except (TypeError, AttributeError): argspec = None if argspec: assert argspec[2] is not None, \ "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: receiver = saferef.safeRef(receiver, onDelete=self._remove_receiver) with self.lock: for r_key, _ in self.receivers: if r_key == lookup_key: break else: self.receivers.append((lookup_key, receiver)) def disconnect(self, receiver=None, sender=None, weak=True, dispatch_uid=None): if dispatch_uid: loopup_key = (dispatch_uid, _make_id(sender)) else: loopup_key = (_make_id(receiver), _make_id(sender)) with self.lock: for index in xrange(len(self.receivers)): (r_key, _) = self.receivers[index] if r_key == loopup_key: del self.receivers[index] break def has_listeners(self, sender=None): return bool(self._live_receivers(_make_id(sender))) def send(self, sender, **named): responses = [] if not self.receivers: return responses for receiver in self._live_receivers(_make_id(sender)): response = receiver(signal=self, sender=sender, **named) responses.append((receiver, response)) return responses def _live_receivers(self, senderkey): none_senderkey = _make_id(None) receivers = [] for (receiverKey, r_senderkey), receiver in self.receivers: if r_senderkey == none_senderkey or r_senderkey == senderkey: if isinstance(receiver, WEAKREF_TYPES): receiver = receiver() if receiver is not None: receivers.append(receiver) else: receivers.append(receiver) return receivers def _remove_receiver(self, receiver): with self.lock: to_remove = [] for key, connected_receiver in self.receivers: if connected_receiver == receiver: to_remove.append(key) for key in to_remove: last_idx = len(self.receivers) - 1 for idx, (r_key, _) in enumerate(reversed(self.receivers)): if r_key == key: del self.receivers[last_idx - idx] signal_demo = Signal() def reset_queries(**kwargs): print 'reset_querys [%s]' % str(kwargs) signal_demo.connect(reset_queries) class objDemo(object): def click(self): signal_demo.send(sender=self.__class__, request={"1":1}) obj = objDemo() obj.click()
reset_querys [{'signal': <__main__.Signal object at 0x109be5290>, 'request': {'1': 1}, 'sender': <class '__main__.objDemo'>}]