溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

深入理解Django-Signals信號量

發(fā)布時間:2020-10-04 12:19:04 來源:腳本之家 閱讀:247 作者:二十一 欄目:開發(fā)技術(shù)

定義Signals

Django自身提供了一些常見的signal,用戶本身也可以定義自己需要的signal

定義signal很簡單,只需要實例化一個Signal實例即可

實例化Signal時,可以傳入關(guān)鍵詞參數(shù)providing_args, providing_args是一個列表,列表中定義了當前signal調(diào)用send方法時可以傳入的參數(shù)。

# django.core.signals.py

from django.dispatch import Signal

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定義一個線程鎖

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處理函數(shù)

Signal處理函數(shù)是一個函數(shù)或者是一個實例的方法,并且必須滿足下面條件:

  1. hashable
  2. 可以接收關(guān)鍵詞參數(shù)

其中處理函數(shù)必須包含的關(guān)鍵詞參數(shù)有兩個:

  • signal,要接收的Signal實例
  • sender,要接收的Signal觸發(fā)者
# django.db.__init__.py

from django.core import signals
from django.db.utils import ConnectionHandler

connections = 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)

處理函數(shù)綁定Signal

django提供了兩種方法可以將Signal的處理函數(shù)和Signal實例進行綁定:

  • 手動調(diào)用connect方法
  • 使用裝飾器receiver

其實裝飾器receiver最終還是調(diào)用了connect方法將處理函數(shù)和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()

每個信號量根據(jù)receiver和sender都可以獲取一個lookup_key可以唯一的標志一個Signal和其處理方法, 當調(diào)用Signal實例的connect方法時,會判斷綁定的處理函數(shù)是否已經(jīng)在自身receivers中,如果存在則不會重復(fù)注冊

發(fā)送Singal

有了前面定義的Signal實例,以及定義的Signal實例處理方法,經(jīng)過處理函數(shù)綁定Signal實例后就可以在必要的地方發(fā)送信號, 然后讓綁定的處理函數(shù)處理了。

# django.core.handlers.wsgi.py

from threading import Lock

from django.core import signals
from 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的一個核心知識點,在項目中很少有使用到,所以很多人都不了解或者沒聽過過(包括我)。簡單來說就是在進行一些操作的前后我們可以發(fā)出一個信號來獲得特定的操作,這些操作包括

django.db.models.signals.pre_save&django.db.models.signals.post_save
在模型 save()方法調(diào)用之前或之后發(fā)送。

django.db.models.signals.pre_delete&django.db.models.signals.post_delete
在模型delete()方法或查詢集的delete() 方法調(diào)用之前或之后發(fā)送。

django.db.models.signals.m2m_changed
模型上的 ManyToManyField 修改時發(fā)送。

django.core.signals.request_started&django.core.signals.request_finished
Django開始或完成HTTP請求時發(fā)送。

其他細致的知識點,大家可以點鏈接查看,直接通過一個例子解釋:

在自定義用戶模型類的時候,在后臺添加用戶數(shù)據(jù)因為使用了自定義模型類的create所以密碼會以明文保存,接下來使用信號量方式在保存后馬上修改密碼解決。(網(wǎng)上一個項目的例子)

users/signals.py

from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth import get_user_model
User = get_user_model()


# post_save:上面七大方法之一:在模型保存之后的操作
# sender: 發(fā)出信號的model
@receiver(post_save, sender=User)
def create_user(sender, instance=None, created=False, **kwargs):
  """
  sender:模型類。
  instance:保存的實際實例。
  created:如果創(chuàng)建了新記錄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 AppConfig


class UsersConfig(AppConfig):
  name = 'users'
  verbose_name = '用戶管理'

  def ready(self):
  """使用ready加載,否則不生效"""
    import users.signals

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI