如何在Django中阻止post_save递归?

时间:2016-09-14 02:28:28

标签: django django-signals

在Django中使用signal时遇到一些问题。

由于函数内部post_save

instance.save()发生递归。

但奇怪的是只有一种情况发生递归。

  1. 不发生递归的情况。
  2. 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()时,它不会发生递归。

    1. 发生递归的案例
    2. 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
      

      我认为这两种情况具有相同的结构,但只有一种情况发生递归。

      不知道为什么。需要帮助,谢谢。

4 个答案:

答案 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()方法。