欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Django中的Signal代碼詳解

 更新時間:2018年02月05日 14:21:31   作者:SuPhoebe  
這篇文章主要介紹了Django中的Signal代碼詳解,分享了相關代碼示例,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下

本文研究的主要是Django開發(fā)中的signal 的相關內容,具體如下。

前言

在web開發(fā)中, 你可能會遇到下面這種場景:

在用戶完成某個操作后, 自動去執(zhí)行一些后續(xù)的操作. 譬如用戶完成修改密碼后,
你要發(fā)送一份確認郵件.

當然可以把邏輯寫在一起,但是有個問題是,觸發(fā)操作一般不止一種(如用戶更改了其它信息的確認郵件),這時候這個邏輯會需要寫多次,所以你可能會想著DRY(Don't repeat yourself),于是你把它寫到了一個函數中,每次調用。當然這是沒問題的.

但是, 如果你換個思路你會發(fā)現另一個完全不同的方案, 即:

  • 類似于daemon的程序監(jiān)聽著特定的事件
  • 前置操作來觸發(fā)相應的事件
  • 監(jiān)聽程序執(zhí)行對應的操作

這樣的好處是什么呢?

  • 松耦合(不用把后續(xù)操作寫在主邏輯中)
  • 便于復用(這也是為什么django本身,及第三方應用如pinax大量使用此技術的原因),在各種高級語言中都會有類似的特性,如java,javascript等,而在django中我們使用signal。

觀察者模式

Siganl是Django框架中提供的一個 “信號分發(fā)器”。它是設計模式中經常提到的觀察者模式的一個實現應用。

在此種模式中,一個目標物件管理所有相依于它的觀察者物件,并且在它本身的狀態(tài)改變時主動發(fā)出通知。這通常透過呼叫各觀察者所提供的方法來實現。

觀察者模式的使用場景

  • 關聯行為場景,需要注意的是,關聯行為是可拆分的,而不是“組合”關系。
  • 事件多級觸發(fā)場景。
  • 跨系統(tǒng)的消息交換場景,如消息隊列、事件總線的處理機制。

優(yōu)點

1.解除耦合,讓耦合的雙方都依賴于抽象,從而使得各自的變換都不會影響另一邊的變換。

它在被觀察者和觀察者之間建立一個抽象的耦合。被觀察者角色所知道的只是一個具體觀察者列表,每一個具體觀察者都符合一個抽象觀察者的接口。被觀察者并不認識任何一個具體觀察者,它只知道它們都有一個共同的接口。

由于被觀察者和觀察者沒有緊密地耦合在一起,因此它們可以屬于不同的抽象化層次。這種耦合性使得代碼的可讀性、維護性大大提高。

2.觀察者模式實現了動態(tài)聯動;

由于觀察者模式對觀察者注冊實行管理,那就可以在運行期間,通過動態(tài)的控制注冊的觀察者來控制某個動作的聯動范圍,從而實現動態(tài)聯動。

3.觀察者模式支持廣播通信。

目標發(fā)送通知給觀察者是面向所有注冊的觀察者,所以目標每次通知的信息就要對所有注冊的觀察者進行廣播,也可以在目標上添加新的方法來限制廣播的范圍。

Django 中Siganl 機制的典型應用是,框架為 Models 創(chuàng)建了 pre_save、post_save等與Model的某些方法調用相關聯的信號,如pre_save 和 post_save 分別會在 Modle的save()方法的調用之前和之后通知觀察者,從而讓觀察者進行一系列操作。

django signal的處理是同步的,勿用于處理大批量任務。
django signal對程序的解耦、代碼的復用及維護性有很大的幫助。

Signal 機制的實現方式

Siganl的源碼位于django dispatch包下,主要的代碼位于 dispatcher.py中。

在dispatcher中定義了Signal類,以及一個用于使用Python裝飾器的方式來連接信號以及信號接受者的方法receiver(signal,**kwargs)。

class Signal(object):
  """
  Base class for all signals

  Internal attributes:

    receivers
      { receiverkey (id) : weakref(receiver) }
  """
  def __init__(self, providing_args=None, use_caching=False):
    """
    創(chuàng)建一個新的Signal
    providing_args 參數,指定這個Siganl 在發(fā)出事件(調用send方法)時,可以提供給觀察者的信息參數
    比如 post_save()會帶上 對應的instance對象,以及update_fields等
    """
    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

  def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):

    from django.conf import settings

    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
      # 構造弱引用的的receiver
      if hasattr(receiver, '__self__') and hasattr(receiver, '__func__'):
        ref = WeakMethod
        receiver_object = receiver.__self__
      if sys.version_info >= (3, 4):
        receiver = ref(receiver)
        weakref.finalize(receiver_object, self._remove_receiver)
      else:
        receiver = ref(receiver, self._remove_receiver)

    with self.lock:
      #clear掉 由于弱引用 已被垃圾回收期回收的receivers
      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()

  def disconnect(self, receiver=None, sender=None, weak=True, dispatch_uid=None):

    if dispatch_uid:
      lookup_key = (dispatch_uid, _make_id(sender))
    else:
      lookup_key = (_make_id(receiver), _make_id(sender))

    disconnected = False
    with self.lock:
      self._clear_dead_receivers()
      for index in range(len(self.receivers)):
        (r_key, _) = self.receivers[index]
        if r_key == lookup_key:
          disconnected = True
          del self.receivers[index]
          break
      self.sender_receivers_cache.clear()
    return disconnected

  def has_listeners(self, sender=None):
    return bool(self._live_receivers(sender))

  def send(self, sender, **named):

    responses = []
    if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS:
      return responses

    for receiver in self._live_receivers(sender):
      response = receiver(signal=self, sender=sender, **named)
      responses.append((receiver, response))
    return responses

  def send_robust(self, sender, **named):

    responses = []
    if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS:
      return responses

    # Call each receiver with whatever arguments it can accept.
    # Return a list of tuple pairs [(receiver, response), ... ].
    for receiver in self._live_receivers(sender):
      try:
        response = receiver(signal=self, sender=sender, **named)
      except Exception as err:
        if not hasattr(err, '__traceback__'):
          err.__traceback__ = sys.exc_info()[2]
        responses.append((receiver, err))
      else:
        responses.append((receiver, response))
    return responses

  def _clear_dead_receivers(self):
    # Note: caller is assumed to hold self.lock.
    if self._dead_receivers:
      self._dead_receivers = False
      new_receivers = []
      for r in self.receivers:
        if isinstance(r[1], weakref.ReferenceType) and r[1]() is None:
          continue
        new_receivers.append(r)
      self.receivers = new_receivers

  def _live_receivers(self, sender):
    """
    過濾掉 已經被 垃圾回收的receiver
    """
    receivers = None
    # 如果使用了cache , 并且沒有調用過_remove_receiver 函數 則去 sender_receivers_cache中查找
    if self.use_caching and not self._dead_receivers:
      receivers = self.sender_receivers_cache.get(sender)
      # We could end up here with NO_RECEIVERS even if we do check this case in
      # .send() prior to calling _live_receivers() due to concurrent .send() call.
      if receivers is NO_RECEIVERS:
        return []
    if receivers is None:
      with self.lock:
        self._clear_dead_receivers()
        senderkey = _make_id(sender)
        receivers = []
        for (receiverkey, r_senderkey), receiver in self.receivers:
          if r_senderkey == NONE_ID or r_senderkey == senderkey:
            receivers.append(receiver)
        if self.use_caching:
          if not receivers:
            self.sender_receivers_cache[sender] = NO_RECEIVERS
          else:
            # Note, we must cache the weakref versions.
            self.sender_receivers_cache[sender] = receivers
    non_weak_receivers = []
    for receiver in receivers:
      if isinstance(receiver, weakref.ReferenceType):
        # Dereference the weak reference.
        receiver = receiver()
        if receiver is not None:
          non_weak_receivers.append(receiver)
      else:
        non_weak_receivers.append(receiver)
    return non_weak_receivers

  def _remove_receiver(self, receiver=None):

    self._dead_receivers = True

connect方法

connect方法用于連接信號和信號處理函數,類似的概念相當于為某個事件(信號發(fā)出表示一個事件)注冊觀察者(處理函數),函數參數中receiver就是信號處理函數(函數也是對象,這太方便了),sender表示信號的發(fā)送者,比如Django框架中的post_save()這個信號,任何一個模型在save()函數調用之后都會發(fā)出這個信號,但是我們只想關注某一個模型 save()方法調用的事件發(fā)生,就可以指定sender為我們需要關注的模型類。

weak參數表示是否將receiver轉換成弱引用對象,Siganl中默認會將所有的receiver轉成弱引用,所以如果你的receiver是個局部對象的話,那么receiver可能會被垃圾回收期回收,receiver也就變成一個dead_receiver了,Siganl會在connect和disconnect方法調用的時候,清除dead_receiver。

dispatch_uid,這個參數用于唯一標識這個receiver函數,主要的作用是防止receiver函數被注冊多次,這樣會導致receiver函數會執(zhí)行多次,這可能是我們不想要的一個結果。

disconnect方法

  disconnect方法用于斷開信號的接收器,函數內首先會生成根據sender和receiver對象構造出的一個標識lookup_key,在遍歷receiver數組時,根據lookup_key找到需要disconnect的receiver然后從數組中刪除這個receiver。

send和send_robust

send和send_robust方法都是用于發(fā)送事件的函數,不同點在于send_robust函數中會捕獲信號接收函數發(fā)生的異常,添加到返回的responses數組中。

Siganl類的使用

Django signal的處理過程如下圖所示:

內建signal的使用

模型相關:

  • pre_save 對象save前觸發(fā)
  • post_save 對象save后觸發(fā)
  • pre_delete 對象delete前觸發(fā)
  • post_delete 對象delete后觸發(fā)
  • m2m_changed ManyToManyField 字段更新后觸發(fā)

請求相關:

  • request_started 一個request請求前觸發(fā)
  • request_finished request請求后觸發(fā)

針對django自帶的signal,我們只需要編寫receiver 即可,使用如下。

第一步,編寫receiver并綁定到signal

# myapp/signals/handlers.py

from django.dispatch import receiver
from django.core.signals import request_finished

## decorators 方式綁定
@receiver(request_finished, dispatch_uid="request_finished")
def my_signal_handler(sender, **kwargs):
  print("Request finished!================================")

# 普通綁定方式
def my_signal_handler(sender, **kwargs):
  print("Request finished!================================")

request_finished.connect(my_signal_handler)

#####################################################
# 針對model 的signal 
from django.dispatch import receiver
from django.db.models.signals import post_save

from polls.models import MyModel


@receiver(post_save, sender=MyModel, dispatch_uid="mymodel_post_save")
def my_model_handler(sender, **kwargs):
 print('Saved: {}'.format(kwargs['instance'].__dict__))

dispatch_uid確保此receiver只調用一次

第二步,加載signal

# myapp/__init__py

default_app_config = 'myapp.apps.MySendingAppConfig'
# myapp/apps.py

from django.apps import AppConfig


class MyAppConfig(AppConfig):
  name = 'myapp'

  def ready(self):
    # signals are imported, so that they are defined and can be used
    import myapp.signals.handlers

到此,當系統(tǒng)受到request 請求完成后,便會執(zhí)行receiver。

其他內建的signal,參考官方文檔:

https://docs.djangoproject.com/en/1.9/topics/signals/

自定義signal的使用

自定義signal,需要我們編寫signal和receiver。

第一步,編寫signal

myapp.signals.signals.py

importdjango.dispatch

my_signal = django.dispatch.Signal(providing_args=["my_signal_arg1", "my_signal_arg_2"])

第二步,加載signal

# myapp/__init__py

default_app_config = 'myapp.apps.MySendingAppConfig'
myapp/apps.py

from django.apps import AppConfig


class MyAppConfig(AppConfig):
  name = 'myapp'

  def ready(self):
    # signals are imported, so that they are defined and can be used
    import myapp.signals.handlers

第三步,事件觸發(fā)時,發(fā)送signal

# myapp/views.py

from .signals.signals import my_signal

my_signal.send(sender="some function or class",
        my_signal_arg1="something", my_signal_arg_2="something else"])

自定義的signal,django已經為我們編寫了此處的事件監(jiān)聽。

第四步,收到signal,執(zhí)行receiver

# myapp/signals/handlers.py

from django.dispatch import receiver
from myapp.signals.signals import my_signal


@receiver(my_signal, dispatch_uid="my_signal_receiver")
def my_signal_handler(sender, **kwargs):
  print('my_signal received')

此時,我們自定義的signal 便開發(fā)完成了。

總結

以上就是本文關于Django中的Signal代碼詳解的全部內容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!

相關文章

  • python opencv 直方圖反向投影的方法

    python opencv 直方圖反向投影的方法

    這篇文章主要介紹了python opencv 直方圖反向投影的方法,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-02-02
  • 使用Python進行數獨求解詳解(二)

    使用Python進行數獨求解詳解(二)

    對于利用Python求解數獨,我們可以采用回溯算法實現一個簡單的版本。本文將此基礎上,通過改進來提升數獨問題求解算法的性能。需要的可以參考一下
    2022-02-02
  • python實現五子棋游戲

    python實現五子棋游戲

    這篇文章主要為大家詳細介紹了python實現五子棋游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-06-06
  • Python列表list的詳細用法介紹

    Python列表list的詳細用法介紹

    這篇文章主要介紹了Python列表list的詳細用法介紹,列表(list)作為Python中基本的數據結構,是存儲數據的容器,相當于其它語言中所說的數組
    2022-07-07
  • Python畫圖時如何調用本地字體

    Python畫圖時如何調用本地字體

    這篇文章主要為大家介紹在通過Python繪制圖畫時如何調用本地的字體,從而解決中文亂碼的問題。感興趣的小伙伴快來跟隨小編學習學習吧
    2021-12-12
  • 教你從零開始實現貪吃蛇Python小游戲

    教你從零開始實現貪吃蛇Python小游戲

    這篇文章主要教你從零開始實現貪吃蛇Python小游戲,沒有使用pygame庫,附帶源碼和注釋,非常有意思,需要的朋友可以參考下
    2023-03-03
  • opencv實現圖片模糊和銳化操作

    opencv實現圖片模糊和銳化操作

    這篇文章主要為大家詳細介紹了opencv實現圖片模糊和銳化操作,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-11-11
  • django API 中接口的互相調用實例

    django API 中接口的互相調用實例

    這篇文章主要介紹了django API 中接口的互相調用實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-04-04
  • python 讀取txt中每行數據,并且保存到excel中的實例

    python 讀取txt中每行數據,并且保存到excel中的實例

    下面小編就為大家分享一篇python 讀取txt中每行數據,并且保存到excel中的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-04-04
  • 用Python的Tornado框架結合memcached頁面改善博客性能

    用Python的Tornado框架結合memcached頁面改善博客性能

    這篇文章主要介紹了用Python的Tornado框架結合memcached頁面改善vLog性能,主要使用到了緩存來提升性能,需要的朋友可以參考下
    2015-04-04

最新評論