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")

定义signal

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