在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'>}]