中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

深入理解Django-Signals信號量

發布時間:2020-10-04 12:19:04 來源:腳本之家 閱讀:248 作者:二十一 欄目:開發技術

定義Signals

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

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

實例化Signal時,可以傳入關鍵詞參數providing_args, providing_args是一個列表,列表中定義了當前signal調用send方法時可以傳入的參數。

# 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處理函數

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

  1. hashable
  2. 可以接收關鍵詞參數

其中處理函數必須包含的關鍵詞參數有兩個:

  • signal,要接收的Signal實例
  • sender,要接收的Signal觸發者
# 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)

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

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_save
from django.dispatch import receiver
from django.contrib.auth import get_user_model
User = 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 AppConfig


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

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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

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

AI

织金县| 额尔古纳市| 始兴县| 红桥区| 景东| 乌拉特前旗| 大悟县| 漳平市| 五大连池市| 伊川县| 绥中县| 泰州市| 甘泉县| 巴彦淖尔市| 托克逊县| 团风县| 牙克石市| 岳阳市| 望奎县| 南投县| 台安县| 临邑县| 贵港市| 安仁县| 安康市| 柏乡县| 宜兴市| 弥渡县| 普定县| 达州市| 南岸区| 涟水县| 邳州市| 彰化市| 察雅县| 蒙自县| 南澳县| 平山县| 德保县| 和顺县| 应城市|