使用post_save信号创建含通用外键的Notification模型实例时遭遇AttributeError问题求助
post_save信号创建含通用外键的Notification模型实例时遭遇AttributeError问题求助
大家好,我最近在Django项目里遇到了一个通用外键结合信号的问题,折腾了好久没搞定,想请各位帮忙看看:
我现在在处理三个模型:SurveyQuestion、Update和Notification。我的需求很明确:每当SurveyQuestion或者Update的实例被创建时,通过post_save信号自动生成一个Notification实例,并且让Notification通过通用外键关联到触发它的那个源模型实例。
我在Notification模型里定义了GenericForeignKey,还想在__str__方法里显示源实例的title字段,代码片段是这样的:
class Notification(models.Model): source_object = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveIntegerField() source = GenericForeignKey("source_object", "object_id") # 其他字段... def __str__(self): return f'{self.source.title} notification'
现在的问题是:我能从后台正常创建SurveyQuestion和Update的实例,看起来Notification也被同步创建了,但当我在shell里查询Notification实例时,直接抛出了AttributeError:
from hotline.models import Notification notifications = Notification.objects.all() for notification in notifications: print(f"Notification object: {notification}") # 报错信息: AttributeError: 'NoneType' object has no attribute 'title'
更奇怪的是,当我尝试打印notification.source的类名时,结果全都是NoneType:
for notification in notifications: print(f"Notification for {notification.source.__class__.__name__}") # 输出: Notification for NoneType Notification for NoneType Notification for NoneType
我之前还踩过一个坑:一开始在信号函数里创建Notification时,我直接传了source_object=instance,结果保存时直接报错:
ValueError at /admin/hotline/update/add/ Cannot assign "<Update: Update - ad>": "Notification.source_object" must be a "ContentType" instance.
后来我改成了source_object=ContentType.objects.get_for_model(instance),这个错误倒是解决了,但现在又出现了源对象为空的问题。
下面是我的完整models.py代码,麻烦各位帮忙排查下问题出在哪:
from django.db import models from datetime import timedelta from django.utils import timezone from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation from django.contrib.contenttypes.models import ContentType from django.db.models.signals import post_save from django.dispatch import receiver def tmrw(): return timezone.now() + timedelta(days=1) class Notification(models.Model): source_object = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveIntegerField() source = GenericForeignKey("source_object", "object_id") start_date = models.DateTimeField(default=timezone.now) end_date = models.DateTimeField(default=tmrw) class Meta: verbose_name = 'Notification' verbose_name_plural = f'{verbose_name}s' def __str__(self): return f'{self.source.title} notification' class Update(models.Model): title = models.CharField(max_length=25) update = models.TextField() start_date = models.DateTimeField(default=timezone.now) end_date = models.DateTimeField(default=tmrw) #notification = GenericRelation(Notification, related_query_name='notification') class Meta: verbose_name = 'Update' verbose_name_plural = f'{verbose_name}s' def __str__(self): return f'{self.__class__.__name__} - {self.title}' class SurveyQuestion(models.Model): title = models.CharField(max_length=25) question = models.TextField() start_date = models.DateTimeField(default=timezone.now) end_date = models.DateTimeField(default=tmrw) #notification = GenericRelation(Notification, related_query_name='notification') class Meta: verbose_name = 'Survey' verbose_name_plural = f'{verbose_name}s' def __str__(self): return f'{self.__class__.__name__} - {self.title}' class SurveyOption(models.Model): survey = models.ForeignKey(SurveyQuestion, on_delete=models.CASCADE, related_name='options') option = models.TextField() id = models.AutoField(primary_key=True) class Meta: verbose_name = 'Survey option' verbose_name_plural = f'{verbose_name}s' def __str__(self): return f'{self.survey.title} option #{self.id}' @receiver(post_save) def create_notification(instance, **kwargs): #"""" print (f"instance: {instance}") print (f"instance.__class__: {instance.__class__}") print (f"instance.__class__.__name__: {instance.__class__.__name__}") #"""" senders = ['SurveyQuestion', 'Update'] if instance.__class__.__name__ in senders: notification = Notification(source_object=ContentType.objects.get_for_model(instance), start_date=instance.start_date, end_date=instance.end_date) notification.save() post_save.connect(create_notification)
备注:内容来源于stack exchange,提问作者Kovy Jacob
相关产品推荐
相关产品推荐

