在Django中使用signal
时遇到一些问题。
post_save
, instance.save()
发生递归。
但奇怪的是只有一种情况发生递归。
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
创建Product.objects.create()
时,它不会发生递归。
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
我认为这两种情况具有相同的结构,但只有一种情况发生递归。
不知道为什么。需要帮助,谢谢。
答案 0 :(得分:7)
保存前断开信号,然后重新连接。 https://docs.djangoproject.com/en/1.10/topics/signals/#disconnecting-signals
def post_save_product(sender, instance, **kwargs):
post_save.disconnect(post_save_product, sender=sender)
instance.do_stuff()
instance.save()
post_save.connect(post_save_product, sender=sender)
post_save.connect(post_save_product, sender= Product)
答案 1 :(得分:2)
在第二种情况下,您将dojo/store/JsonRest
的数据库值与显示值进行比较。这些永远不会匹配。您应该检查数据库值:
instance.color
同样,您应将默认值设置为数据库值,即@receiver(post_save, sender=Variation)
def post_save_variation(sender, instance, created, **kwargs):
if not instance.price:
if instance.color == 'black':
instance.price = 40000
elif instance.color == 'single':
instance.price = 50000
elif instance.color == 'multi':
instance.price = 60000
instance.save()
。
在原始代码中,所有检查都将失败,default = 'black'
永远不会更新为非空值。对instance.price
的调用将再次触发instance.save()
信号,post_save
仍为真,并且在不设置价格的情况下再次保存实例。这是你所看到的无限递归。
在第一个示例中,not instance.price
始终设置为非空值,因此当第二次触发slug
信号时,post_save
检查将失败,并且该实例不会第三次保存。
在这两种情况下,如果没有设置段塞/价格,您至少要保存实例两次。为防止这种情况,您可以使用if not instance.slug
信号。您不必再在信号处理程序中保存实例:
pre_save
答案 2 :(得分:2)
如果您想避免post_save
信号中的递归,只需使用Model.objects.filter(id=id).update(object=object)
答案 3 :(得分:0)
只需使用pre_save
,就无需在其内部再次使用.save()
方法。