深入理解Django-Signals信号量
定义Signals
Django自身提供了一些常见的signal,用户本身也可以定义自己需要的signal
定义signal很简单,只需要实例化一个Signal实例即可
实例化Signal时,可以传入关键词参数providing_args,providing_args是一个列表,列表中定义了当前signal调用send方法时可以传入的参数。
#django.core.signals.py fromdjango.dispatchimportSignal request_started=Signal(providing_args=["environ"]) request_finished=Signal() got_request_exception=Signal(providing_args=["request"]) setting_changed=Signal(providing_args=["setting","value","enter"])
其中Signal的初始化也比较简单,就是为实例化的signal定义一个线程锁
classSignal(object): def__init__(self,providing_args=None,use_caching=False): self.receivers=[] ifproviding_argsisNone: providing_args=[] self.providing_args=set(providing_args) self.lock=threading.Lock() self.use_caching=use_caching #Forconveniencewecreateemptycacheseveniftheyarenotused. #Anoteaboutcaching:ifuse_cachingisdefined,thenforeach #distinctsenderwecachethereceiversthatsenderhasin #'sender_receivers_cache'.Thecacheiscleanedwhen.connect()or #.disconnect()iscalledandpopulatedonsend(). self.sender_receivers_cache=weakref.WeakKeyDictionary()ifuse_cachingelse{} self._dead_receivers=False
定义Signal处理函数
Signal处理函数是一个函数或者是一个实例的方法,并且必须满足下面条件:
- hashable
- 可以接收关键词参数
其中处理函数必须包含的关键词参数有两个:
- signal,要接收的Signal实例
- sender,要接收的Signal触发者
#django.db.__init__.py fromdjango.coreimportsignals fromdjango.db.utilsimportConnectionHandler connections=ConnectionHandler() defreset_queries(**kwargs): forconninconnections.all(): conn.queries_log.clear() signals.request_started.connect(reset_queries) defclose_old_connections(**kwargs): forconninconnections.all(): conn.close_if_unusable_or_obsolete() signals.request_started.connect(close_old_connections) signals.request_finished.connect(close_old_connections)
处理函数绑定Signal
django提供了两种方法可以将Signal的处理函数和Signal实例进行绑定:
- 手动调用connect方法
- 使用装饰器receiver
其实装饰器receiver最终还是调用了connect方法将处理函数和Signal实例进行绑定
Signal类的connect方法定义如下:
classSignal(object): ... defconnect(self,receiver,sender=None,weak=True,dispatch_uid=None): fromdjango.confimportsettings #IfDEBUGison,checkthatwegotagoodreceiver ifsettings.configuredandsettings.DEBUG: assertcallable(receiver),"Signalreceiversmustbecallable." #Checkfor**kwargs ifnotfunc_accepts_kwargs(receiver): raiseValueError("Signalreceiversmustacceptkeywordarguments(**kwargs).") ifdispatch_uid: lookup_key=(dispatch_uid,_make_id(sender)) else: lookup_key=(_make_id(receiver),_make_id(sender)) ifweak: ref=weakref.ref receiver_object=receiver #Checkforboundmethods ifhasattr(receiver,'__self__')andhasattr(receiver,'__func__'): ref=WeakMethod receiver_object=receiver.__self__ ifsix.PY3: receiver=ref(receiver) weakref.finalize(receiver_object,self._remove_receiver) else: receiver=ref(receiver,self._remove_receiver) withself.lock: self._clear_dead_receivers() forr_key,_inself.receivers: ifr_key==lookup_key: break else: self.receivers.append((lookup_key,receiver)) self.sender_receivers_cache.clear()
每个信号量根据receiver和sender都可以获取一个lookup_key可以唯一的标志一个Signal和其处理方法,当调用Signal实例的connect方法时,会判断绑定的处理函数是否已经在自身receivers中,如果存在则不会重复注册
发送Singal
有了前面定义的Signal实例,以及定义的Signal实例处理方法,经过处理函数绑定Signal实例后就可以在必要的地方发送信号,然后让绑定的处理函数处理了。
#django.core.handlers.wsgi.py fromthreadingimportLock fromdjango.coreimportsignals fromdjango.core.handlersimportbase classWSGIHandler(base.BaseHandler): ... def__call__(self,environ,start_response): ... signals.request_started.send(sender=self.__class__,environ=environ) ...
信号量最为Django的一个核心知识点,在项目中很少有使用到,所以很多人都不了解或者没听过过(包括我)。简单来说就是在进行一些操作的前后我们可以发出一个信号来获得特定的操作,这些操作包括
django.db.models.signals.pre_save&django.db.models.signals.post_save
在模型save()方法调用之前或之后发送。
django.db.models.signals.pre_delete&django.db.models.signals.post_delete
在模型delete()方法或查询集的delete()方法调用之前或之后发送。
django.db.models.signals.m2m_changed
模型上的ManyToManyField修改时发送。
django.core.signals.request_started&django.core.signals.request_finished
Django开始或完成HTTP请求时发送。
其他细致的知识点,大家可以点链接查看,直接通过一个例子解释:
在自定义用户模型类的时候,在后台添加用户数据因为使用了自定义模型类的create所以密码会以明文保存,接下来使用信号量方式在保存后马上修改密码解决。(网上一个项目的例子)
users/signals.py
fromdjango.db.models.signalsimportpost_save fromdjango.dispatchimportreceiver fromdjango.contrib.authimportget_user_model User=get_user_model() #post_save:上面七大方法之一:在模型保存之后的操作 #sender:发出信号的model @receiver(post_save,sender=User) defcreate_user(sender,instance=None,created=False,**kwargs): """ sender:模型类。 instance:保存的实际实例。 created:如果创建了新记录True。 update_fields:Model.save()要更新的字段集,如果没有传递则为None """ ifcreated: password=instance.password #instance相当于user instance.set_password(password) instance.save()
users/apps.py
fromdjango.appsimportAppConfig classUsersConfig(AppConfig): name='users' verbose_name='用户管理' defready(self): """使用ready加载,否则不生效""" importusers.signals
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。