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_clearreverse
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