Skip to content

Latest commit

 

History

History
133 lines (113 loc) · 4.51 KB

signal信号.md

File metadata and controls

133 lines (113 loc) · 4.51 KB

Signal

Xiang Wang @ 2017-05-24 16:32:47

官方教程 参考

源码剖析

注册一个post_save后,就会在receivers里面添加对应的key和函数. 如果下次sender来了,就根据_make_id来判断是否要执行. 这会导致dispatch无法处理proxy的model. 想解决?12年前就提出来了,没采纳

def _make_id(target):
    if hasattr(target, "__func__"):
        return (id(target.__self__), id(target.__function__))
    return id(target)
class Signal:

    def send(self, sender, **named):
        return [
            (receiver, receiver(signal=self, sender=sender, **named)
            for receiver in self._live_receivers(sender)
        ]

    def _live_receivers(self, sender):
        senderkey = _make_id(sender)
        for (receiverkey, r_senderkey), receiver in self.receivers:
            if r_senderkey == NONE_ID or r_senderkey = senderkey:
                receivers.append(receiver)

Listening to signals

  • preventing duplicate signals 每次绑定的时候,同样的函数只会绑定一次。多个函数会按照绑定的顺序依次执行。 如果你希望一个函数绑定2次,需要添加dispatch_uid参数
from django.core.signals import request_finished  
request_finished.connect(my_callback, dispatch_uid="my_unique_identifier")

Defining signals

import django.dispatch

pizza_done = django.dispatch.Signal()
pizza_done.connect(function, sender)

Sending signals

pizza_done.send(sender=self.__class__, toppings=toppings, size=size)

ModelSignals

示例

def my_callback(sender, **kwargs):
    log.info("Request finished!")

def my_signal(sender, *args, **kwargs):
    print(kwargs['instance'])
    print("calling my_signal")

from django.core.signals import request_finished
request_finished.connect(my_callback)

from django.db.models.signals import pre_init
pre_init.connect(my_signal, sender=TestSignal)

pre_init

post_init

pre_save

官方文档

post_save

再用django-rest-framework的ModelSerializer的时候, 因为ModelSerializer的create, 是先创建model,然后设置manytomany的, 所以会导致如果通过post_save来创建, 会导致拿不到关联的manytomany的情况

def create(self, validated_data):
    for field in info.relations.items():
        many_to_many[field_name] = validated_data.pop(field_name)
    instance = create(**validated_data)
    for field_name, value in many_to_many.items():
        getattr(instance, field_name).set(value)
  • 参数:
    • sender: class的类
    • instance: 对象
    • created: 是否是刚创建的
    • update_fields:
      • 这个是Model.save的时候传入的. 不传就是None了
      • 如果是update_or_create,更新时有update_fields

post_delete

  • 如果有外键关联到这个model,那这个外键被删除的时候,触发的联合删除也会触发,并且是先有依赖的外键的model被删除,这个instance本身是最后被删除的
  • 参数:
    • sender
    • instance, 注意此时 instance.id 还是可以获取的
    • using
批量删除的时候每条数据都会触发信号, 但是DELETE语句是只有一句(用的id__in)。所以会导致第一个model的post_delete里面去查询返回的数字是0

m2m_changed

M2Mchanged有点复杂,因为他分为正向和反向的情况,可能是user.books.add(book) 也可能是 book.user_set.remove(user)
每次保存reverse只会触发一个
每次新增pre_add, post_add都会触发一次
直接set也会给每个触发pre_add,post_add,pre_remove,post_remove。但是不变的不会触发
无法拿到被添加的child,因为只有pk_set

  • 示例
from django.db.models.signals import m2m_changed
m2m_changed.connect(function, sender=ManyModel.texts.through)
  • 参数
    • sender
    • instance
    • action: pre_add|post_add|pre_remove|post_remove|pre_clear|post_clear
    • reverse
    • model
    • pk_set
    • using

to be continued

  • Model_signals
    • pre_init
    • post_init
    • post_save
    • pre_delete
    • class_prepared
  • Management signals
  • Request/response signals
  • Test signals
  • database wrappers