Django 信号:与继承自同一模型的模型冲突 class

Django signals: conflicts with models inheritining from the same class

我在将 信号 应用于新模型时遇到了一个奇怪的行为,我不确定是什么问题,但这似乎与我使用 摘要classes.

模型(简化)

基本上,我有文章照片(继承自Post )

class Post(models.Model): 
    class Meta:
        abstract        = True

    some_field    = models.Something()

class Article(Post):
    category = models.ForeignKey(Article_category, null=True, on_delete=models.SET_NULL)
    some_field     = models.Something()

class Photo(Post):
    category = models.ForeignKey(Photo_category, null=True, on_delete=models.SET_NULL)
    some_field     = models.Something()
    

及其各自的类别

class Category(models.Model):
    class Meta: 
        abstract = True 

    parent = models.ForeignKey('self', null=True, blank=True, related_name='nested_category', on_delete=models.SET_NULL)
    name   = models.CharField(max_length=50)
    count  = models.PositiveSmallIntegerField(default=0, editable=False)
    
class Article_category(Category):

    @classmethod
    def load(cls):
        cache.set('{}'.format(cls.__name__), cls.objects.all()) 

class Photo_category(Category):

    @classmethod
    def load(cls):
        cache.set('{}'.format(cls.__name__), cls.objects.all())

信号

一个简单的增量计数器。每次创建 article/photo 时,都会更新相应的类别计数并将整个模型保存在缓存中(用于模板目的)

from django.db.models import F

@receiver(post_save, sender=Article) ----> here comes trouble
@receiver(post_save, sender=Photo)
def add_one_to_count(sender, instance, **kwargs):
    cat = type(instance.category).objects.get(name=instance.category)
    cat.count = F('count')+1
    cat.save()
    cache.set('{}_category'.format(sender.__name__), type(instance.category).objects.all())

问题

你在上面看到的对 @receiver(post_save, sender=Photo) 很有用,但是当我添加 @receiver(post_save, sender=Article) 时,使用 fixture 的数据库初始化失败,我只得到空集表(mariaDB)。这条线是唯一一条从失败到成功的改变,我不明白为什么。由于 count 是在摘要 class 中定义的,我想知道它是否与它有关,因为我对类别应用类似的逻辑没有任何问题:

# this works perfectly
@receiver(post_save, sender=Photo_category)
@receiver(post_delete, sender=Photo_category)
@receiver(post_save, sender=Article_category)
@receiver(post_delete, sender=Article_category)
def refresh_cached_category(sender, instance, using, **kwargs):
    cache.set('{}'.format(type(instance).__name__), type(instance).objects.all())

感谢指教

完整的回溯

 Traceback (most recent call last):
   File "manage.py", line 21, in <module>
     main()
   File "manage.py", line 17, in main
     execute_from_command_line(sys.argv)
   File "/usr/local/lib/python3.7/site-packages/django/core/management/__init__.py", line 381, in execute_from_command_line
     utility.execute()
  File "/usr/local/lib/python3.7/site-packages/django/core/management/__init__.py", line 375, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.7/site-packages/django/core/management/base.py", line 323, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.7/site-packages/django/core/management/base.py", line 364, in execute
    output = self.handle(*args, **options)
  File "/usr/local/lib/python3.7/site-packages/django/core/management/commands/loaddata.py", line 72, in handle
    self.loaddata(fixture_labels)
  File "/usr/local/lib/python3.7/site-packages/django/core/management/commands/loaddata.py", line 114, in loaddata
    self.load_label(fixture_label)
  File "/usr/local/lib/python3.7/site-packages/django/core/management/commands/loaddata.py", line 181, in load_label
    obj.save(using=self.using)
  File "/usr/local/lib/python3.7/site-packages/django/core/serializers/base.py", line 223, in save
    models.Model.save_base(self.object, using=using, raw=True, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/django/db/models/base.py", line 790, in save_base
    update_fields=update_fields, raw=raw, using=using,
  File "/usr/local/lib/python3.7/site-packages/django/dispatch/dispatcher.py", line 175, in send
    for receiver in self._live_receivers(sender)
  File "/usr/local/lib/python3.7/site-packages/django/dispatch/dispatcher.py", line 175, in <listcomp>
    for receiver in self._live_receivers(sender)
  File "/usr/src/cms/website/observers.py", line 26, in add_one_to_count
    cat = type(instance.category).objects.get(name=instance.category)
  File "/usr/local/lib/python3.7/site-packages/django/db/models/manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/django/db/models/query.py", line 408, in get
    self.model._meta.object_name
 website.models.DoesNotExist: Problem installing fixture '/usr/src/cms/../test/data_dev.yaml': Article_category matching query does not exist.

您不能在查询中过滤 name=instance.category,因为那不是 str。您需要过滤 name=instance.category.name 但首先您还需要确保 instance.category 不是 None(因为它可以)。

我不明白的是为什么你首先执行查询,只是为了获取相同的对象:instance.categoryArticleCategory.objects.get(name=instance.category.name) 相同假设名称是唯一的,除了你对数据库做一个额外的查询。

如果您有两个具有相同 name 的类别(您没有在模型中排除),查询也会引发异常。所以你的代码应该是:

def add_one_to_count(sender, instance, **kwargs):
    if instance.category:
        instance.category.count = F('count')+1
        instance.category.save()
        cache.set('{}_category'.format(sender.__name__), type(instance.category).objects.all())