在Django中使用signal
时遇到一些问题。post_save
发生递归,因为函数内部有instance.save()
。
但奇怪的是只有一种情况发生递归。
1.未发生递归的情况。models.py
class Product(TimeStampedModel):
name = models.CharField(max_length=120)
slug = models.SlugField(null=True, blank=True)
description = models.CharField(max_length=400, blank=True)
is_active = models.BooleanField(default=True)
objects = ProductManager()
class Meta:
ordering = ('-created',)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse(
"products:product_detail",
kwargs={
"slug": self.slug,
}
)
signals.py
@receiver(post_save, sender=Product)
def post_save_product(sender, instance, created, **kwargs):
if not instance.slug:
instance.slug = slugify(instance.name, allow_unicode=True)
instance.save()
当我使用Product.objects.create()
创建Product
时,它不会发生递归。
1.出现递归的情况models.py
class Variation(TimeStampedModel):
COLOR_CHOICES = (
('black', '흑백'),
('single', '단색'),
('multi', '컬러'),
)
price = models.DecimalField(
decimal_places=2,
max_digits=15,
blank=True,
null=True,
)
product = models.ForeignKey(Product)
color = models.CharField(
max_length=10,
choices=COLOR_CHOICES,
default='흑백'
)
is_active = models.BooleanField(default=True)
class Meta:
ordering = ('product',)
def __str__(self):
return "{product} - {color}".format(
product=self.product,
color=self.color
)
signals.py
@receiver(post_save, sender=Variation)
def post_save_variation(sender, instance, created, **kwargs):
if not instance.price:
if instance.color == '흑백':
instance.price = 40000
elif instance.color == '단색':
instance.price = 50000
elif instance.color == '컬러':
instance.price = 60000
instance.save()
这种情况下会出现递归错误:
File "/Users/Chois/.pyenv/versions/spacegraphy/lib/python3.5/site-packages/django/db/models/base.py", line 708, in save
force_update=force_update, update_fields=update_fields)
File "/Users/Chois/.pyenv/versions/spacegraphy/lib/python3.5/site-packages/django/db/models/base.py", line 745, in save_base
update_fields=update_fields, raw=raw, using=using)
File "/Users/Chois/.pyenv/versions/spacegraphy/lib/python3.5/site-packages/django/dispatch/dispatcher.py", line 192, in send
response = receiver(signal=self, sender=sender, **named)
File "/Users/Chois/Dropbox/Workspace/django/spacegraphy-project/spacegraphy/products/signals/post_save.py", line 24, in post_save_variation
instance.save()
File "/Users/Chois/.pyenv/versions/spacegraphy/lib/python3.5/site-packages/django/db/models/base.py", line 708, in save
force_update=force_update, update_fields=update_fields)
File "/Users/Chois/.pyenv/versions/spacegraphy/lib/python3.5/site-packages/django/db/models/base.py", line 745, in save_base
update_fields=update_fields, raw=raw, using=using)
File "/Users/Chois/.pyenv/versions/spacegraphy/lib/python3.5/site-packages/django/dispatch/dispatcher.py", line 192, in send
response = receiver(signal=self, sender=sender, **named)
File "/Users/Chois/Dropbox/Workspace/django/spacegraphy-project/spacegraphy/products/signals/post_save.py", line 24, in post_save_variation
instance.save()
File "/Users/Chois/.pyenv/versions/spacegraphy/lib/python3.5/site-packages/django/db/models/base.py", line 708, in save
force_update=force_update, update_fields=update_fields)
File "/Users/Chois/.pyenv/versions/spacegraphy/lib/python3.5/site-packages/django/db/models/base.py", line 736, in save_base
updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
File "/Users/Chois/.pyenv/versions/spacegraphy/lib/python3.5/site-packages/django/db/models/base.py", line 796, in _save_table
base_qs = cls._base_manager.using(using)
File "/Users/Chois/.pyenv/versions/spacegraphy/lib/python3.5/site-packages/django/db/models/manager.py", line 122, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/Users/Chois/.pyenv/versions/spacegraphy/lib/python3.5/site-packages/django/db/models/manager.py", line 214, in get_queryset
return self._queryset_class(model=self.model, using=self._db, hints=self._hints)
File "/Users/Chois/.pyenv/versions/spacegraphy/lib/python3.5/site-packages/django/db/models/query.py", line 171, in __init__
self.query = query or sql.Query(self.model)
File "/Users/Chois/.pyenv/versions/spacegraphy/lib/python3.5/site-packages/django/db/models/sql/query.py", line 155, in __init__
self.where = where()
RecursionError: maximum recursion depth exceeded while calling a Python object
我认为这两种情况有相同的结构,但只有一种情况发生递归。
不知道为什么。需要帮助,谢谢。
5条答案
按热度按时间xa9qqrwz1#
保存前断开信号,然后重新连接。https://docs.djangoproject.com/en/1.10/topics/signals/#disconnecting-signals
sz81bmfz2#
如果你想避免
post_save
信号中的递归,只需要使用Model.objects.filter(id=id).update(object=object)
q5iwbnjs3#
只需使用
pre_save
,您不需要再次在其中使用.save()
方法。8iwquhpp4#
在第二种情况下,您将
instance.color
的数据库值与显示值进行比较。它们永远不会匹配。您应该检查数据库值:同样,您应该将默认值设置为数据库值,即
default = 'black'
。在原始代码中,所有检查都会失败,
instance.price
永远不会更新为非空值。对instance.save()
的调用将再次触发post_save
信号,not instance.price
仍然为true,并且示例再次保存,而不设置价格。这就是您看到的无限递归。在第一个示例中,
slug
始终设置为非空值,因此当第二次触发post_save
信号时,if not instance.slug
检查将失败,并且第三次不会保存示例。在这两种情况下,如果没有设置slug/price,您至少会保存示例两次。为了防止这种情况,您可以使用
pre_save
信号。您不必在信号处理程序中再次保存示例:cnh2zyt35#
你可以做这样的事