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.category
与 ArticleCategory.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())
我在将 信号 应用于新模型时遇到了一个奇怪的行为,我不确定是什么问题,但这似乎与我使用 摘要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.category
与 ArticleCategory.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())