香港云主机最佳企业级服务商!

ADSL拨号VPS包含了中国大陆(联通,移动,电信,)

中国香港,国外拨号VPS。

当前位置:云主机 > python >

电信ADSL拨号VPS
联通ADSL拨号VPS
移动ADSL拨号VPS

深入理解Django-Signals信号量


时间:2022-04-02 10:17 作者:admin


定义Signals

Django自身提供了一些常见的signal,用户本身也可以定义自己需要的signal

定义signal很简单,只需要实例化一个Signal实例即可

实例化Signal时,可以传入关键词参数providing_args, providing_args是一个列表,列表中定义了当前signal调用send方法时可以传入的参数。

# django.core.signals.pyfrom django.dispatch import Signalrequest_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定义一个线程锁

class Signal(object):  def __init__(self, providing_args=None, use_caching=False):    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

定义Signal处理函数

Signal处理函数是一个函数或者是一个实例的方法,并且必须满足下面条件:

    hashable 可以接收关键词参数

其中处理函数必须包含的关键词参数有两个:

signal,要接收的Signal实例 sender,要接收的Signal触发者
# django.db.__init__.pyfrom django.core import signalsfrom django.db.utils import ConnectionHandlerconnections = ConnectionHandler()def reset_queries(**kwargs):  for conn in connections.all():    conn.queries_log.clear()signals.request_started.connect(reset_queries)def close_old_connections(**kwargs):  for conn in connections.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方法定义如下:

class Signal(object):  ...    def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):    from django.conf import settings    # If DEBUG is on, check that we got a good receiver    if settings.configured and settings.DEBUG:      assert callable(receiver), "Signal receivers must be callable."      # Check for **kwargs      if not func_accepts_kwargs(receiver):        raise ValueError("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:      ref = weakref.ref      receiver_object = receiver      # Check for bound methods      if hasattr(receiver, '__self__') and hasattr(receiver, '__func__'):        ref = WeakMethod        receiver_object = receiver.__self__      if six.PY3:        receiver = ref(receiver)        weakref.finalize(receiver_object, self._remove_receiver)      else:        receiver = ref(receiver, self._remove_receiver)    with self.lock:      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()

每个信号量根据receiver和sender都可以获取一个lookup_key可以唯一的标志一个Signal和其处理方法, 当调用Signal实例的connect方法时,会判断绑定的处理函数是否已经在自身receivers中,如果存在则不会重复注册

发送Singal

有了前面定义的Signal实例,以及定义的Signal实例处理方法,经过处理函数绑定Signal实例后就可以在必要的地方发送信号, 然后让绑定的处理函数处理了。

# django.core.handlers.wsgi.pyfrom threading import Lockfrom django.core import signalsfrom django.core.handlers import base class WSGIHandler(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

from django.db.models.signals import post_savefrom django.dispatch import receiverfrom django.contrib.auth import get_user_modelUser = get_user_model()# post_save:上面七大方法之一:在模型保存之后的操作# sender: 发出信号的model@receiver(post_save, sender=User)def create_user(sender, instance=None, created=False, **kwargs):  """  sender:模型类。  instance:保存的实际实例。  created:如果创建了新记录True。  update_fields:Model.save()要更新的字段集,如果没有传递则为None  """  if created:    password = instance.password    # instance相当于user    instance.set_password(password)    instance.save()

users/apps.py

from django.apps import AppConfigclass UsersConfig(AppConfig):  name = 'users'  verbose_name = '用户管理'  def ready(self):  """使用ready加载,否则不生效"""    import users.signals

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

(责任编辑:admin)






帮助中心
会员注册
找回密码
新闻中心
快捷通道
域名登录面板
虚机登录面板
云主机登录面板
关于我们
关于我们
联系我们
联系方式

售前咨询:17830004266(重庆移动)

企业QQ:383546523

《中华人民共和国工业和信息化部》 编号:ICP备00012341号

Copyright © 2002 -2018 香港云主机 版权所有
声明:香港云主机品牌标志、品牌吉祥物均已注册商标,版权所有,窃用必究

云官方微信

在线客服

  • 企业QQ: 点击这里给我发消息
  • 技术支持:383546523

  • 公司总台电话:17830004266(重庆移动)
  • 售前咨询热线:17830004266(重庆移动)