如何在Django模型继承中使用信号?
我在Django中有几个模型继承层级:
class WorkAttachment(models.Model):
""" Abstract class that holds all fields that are required in each attachment """
work = models.ForeignKey(Work)
added = models.DateTimeField(default=datetime.datetime.now)
views = models.IntegerField(default=0)
class Meta:
abstract = True
class WorkAttachmentFileBased(WorkAttachment):
""" Another base class, but for file based attachments """
description = models.CharField(max_length=500, blank=True)
size = models.IntegerField(verbose_name=_('size in bytes'))
class Meta:
abstract = True
class WorkAttachmentPicture(WorkAttachmentFileBased):
""" Picture attached to work """
image = models.ImageField(upload_to='works/images', width_field='width', height_field='height')
width = models.IntegerField()
height = models.IntegerField()
有很多不同的模型是从 WorkAttachmentFileBased
和 WorkAttachment
继承而来的。我想创建一个信号,当附件被创建时,能够更新父级工作的 attachment_count
字段。逻辑上来说,应该认为为父级发送者(WorkAttachment
)创建的信号也会对所有继承的模型生效,但实际上并不是这样。以下是我的代码:
@receiver(post_save, sender=WorkAttachment, dispatch_uid="att_post_save")
def update_attachment_count_on_save(sender, instance, **kwargs):
""" Update file count for work when attachment was saved."""
instance.work.attachment_count += 1
instance.work.save()
有没有办法让这个信号对所有从 WorkAttachment
继承的模型都有效呢?
使用的是Python 2.7,Django 1.4的预发布版本
附注:我尝试过在网上找到的一个解决方案,但对我来说没有效果。
9 个回答
30
最简单的解决办法是,不限制 sender
,而是在信号处理函数里检查相关的实例是否是某个子类。
@receiver(post_save)
def update_attachment_count_on_save(sender, instance, **kwargs):
if isinstance(instance, WorkAttachment):
...
不过,这样做可能会带来比较大的性能开销,因为每次保存任何模型时,上面的函数都会被调用。
我觉得我找到了最符合Django风格的做法:最近版本的Django建议在一个叫 signals.py
的文件里连接信号处理函数。下面是需要的代码:
your_app/__init__.py:
default_app_config = 'your_app.apps.YourAppConfig'
your_app/apps.py:
import django.apps
class YourAppConfig(django.apps.AppConfig):
name = 'your_app'
def ready(self):
import your_app.signals
your_app/signals.py:
def get_subclasses(cls):
result = [cls]
classes_to_inspect = [cls]
while classes_to_inspect:
class_to_inspect = classes_to_inspect.pop()
for subclass in class_to_inspect.__subclasses__():
if subclass not in result:
result.append(subclass)
classes_to_inspect.append(subclass)
return result
def update_attachment_count_on_save(sender, instance, **kwargs):
instance.work.attachment_count += 1
instance.work.save()
for subclass in get_subclasses(WorkAttachment):
post_save.connect(update_attachment_count_on_save, subclass)
我觉得这对所有子类都有效,因为在调用 YourAppConfig.ready
时,所有子类都会被加载(因此 signals
也会被导入)。
55
你可以在注册连接处理程序的时候,不指定sender
。然后在处理程序内部筛选出需要的模型。
from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save)
def my_handler(sender, **kwargs):
# Returns false if 'sender' is NOT a subclass of AbstractModel
if not issubclass(sender, AbstractModel):
return
...
参考链接:https://groups.google.com/d/msg/django-users/E_u9pHIkiI0/YgzA1p8XaSMJ
17
你可以试试下面这样的代码:
model_classes = [WorkAttachment, WorkAttachmentFileBased, WorkAttachmentPicture, ...]
def update_attachment_count_on_save(sender, instance, **kwargs):
instance.work.attachment_count += 1
instance.work.save()
for model_class in model_classes:
post_save.connect(update_attachment_count_on_save,
sender=model_class,
dispatch_uid="att_post_save_"+model_class.__name__)
(声明:我没有测试过上面的代码)