Django post_save()信号实现


72

我有一个关于django的问题。

我这里有许多对多模型

class Product(models.Model):
     name = models.CharField(max_length=255)
     price = models.DecimalField(default=0.0, max_digits=9, decimal_places=2)
     stock = models.IntegerField(default=0)

     def  __unicode__(self):
         return self.name

class Cart(models.Model):
    customer = models.ForeignKey(Customer)
    products = models.ManyToManyField(Product, through='TransactionDetail')
    t_date = models.DateField(default=datetime.now())
    t_sum = models.FloatField(default=0.0)

    def __unicode__(self):
         return str(self.id)

class TransactionDetail(models.Model):
    product = models.ForeignKey(Product)
    cart = models.ForeignKey(Cart)
    amount = models.IntegerField(default=0)

对于创建的1个购物车对象,我可以插入尽可能多的新TransactionDetail对象(产品和金额)。我的问题是。如何实现触发器?我想要的是每当创建交易明细时,我希望产品的存货数量减去交易明细中的数量。

我已经阅读了有关post_save()的信息,但不确定如何实现。也许像这样

什么时候:

post_save(TransactionDetail, 
       Cart) #Cart object where TransactionDetail.cart= Cart.id
Cart.stock -= TransactionDetail.amount

5
如果这样做,您很可能会遇到比赛条件。
Thomas Orozco 2012年

Answers:


165

如果您真的想使用信号来实现这一目标,这里简单介绍一下,

from django.db.models.signals import post_save
from django.dispatch import receiver

class TransactionDetail(models.Model):
    product = models.ForeignKey(Product)

# method for updating
@receiver(post_save, sender=TransactionDetail, dispatch_uid="update_stock_count")
def update_stock(sender, instance, **kwargs):
    instance.product.stock -= instance.amount
    instance.product.save()

1
这对我来说工作正常,但不知道为什么它处于未知长度的循环中
Manoj Sahu 2015年

6
我收到maximum recursion depth exceeded错误消息,因为我将实例本身保存在@receiver函数中。如何实现更新自我模型?我必须重写save()模型方法吗?
Dipak

1
@Dipak超过了最大递归深度,因为每次更新实例时都会触发post_save,因此每次保存时都会调用自身,状态为递归深度值超出异常。让我知道您是如何克服这些的
Vamsidhar Muggulla

3
@VamsidharMuggulla而不是使用signal我重写save模型的方法并使用updated函数更新的模型属性,以便它不会再次触发保存。
Dipak

6
dispatch_uid为了什么?
R11G

18

我个人将覆盖TransactionDetail的save()方法,并在其中保存新的TransactionDetail,然后运行

self.product.stock -= self.amount
self.product.save()

13

如果要避免获取maximum recursion depth exceeded,则应先断开信号连接,然后再保存在信号处理程序中。上面的示例(沈建中的答案)将是:

from django.db.models.signals import post_save
from django.dispatch import receiver

class TransactionDetail(models.Model):
    # ... fields here

# method for updating
@receiver(post_save, sender=TransactionDetail, dispatch_uid="update_stock_count")
def update_stock(sender, instance, **kwargs):
 instance.product.stock -= instance.amount

 post_save.disconnect(update_stock, sender=TransactionDetail)
 instance.product.save()
 post_save.connect(update_stock, sender=TransactionDetail)

为模型断开信号并在django中重新连接中对此进行了详细介绍,并提供了一个更抽象和有用的示例。

另请参阅:django文档中的https://docs.djangoproject.com/en/2.0/topics/signals/#disconnecting-signals


为了这个工作,你需要有dispatch_uiddisconnectconnect方法。返回值可帮助您处理潜在的错误。`is_disconnected = post_save.disconnect(update_stock,sender = TransactionDetail,dispatch_uid =“ update_stock_count”)如果is_diconnected:instance.product.save()is_reconnected = post_save.connect(update_stock,sender = TransactionDetail,dispatch_uid =“ update_stock_count”)`
Califlower

2

如果您真的想在Django中使用信号,请尝试以下操作:

#import inbuilt user model
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver

@receiver(post_save, sender=User)
def create_profile(sender,**kwargs):
    # write you functionality
    pass

然后在初始化文件中添加default_app_config

 default_app_config = "give your AppConfig path"

2

实际上,文档解释在Signalsdjango.dispatch.Signal.connect

def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
    Connect receiver to sender for signal.

    Arguments:

        receiver
            A function or an instance method which is to receive signals.
            Receivers must be hashable objects.

            If weak is True, then receiver must be weak referenceable.

            Receivers must be able to accept keyword arguments.

            If a receiver is connected with a dispatch_uid argument, it
            will not be added if another receiver was already connected
            with that dispatch_uid.

        sender
            The sender to which the receiver should respond. Must either be
            a Python object, or None to receive events from any sender.

        weak
            Whether to use weak references to the receiver. By default, the
            module will attempt to use weak references to the receiver
            objects. If this parameter is false, then strong references will
            be used.

        dispatch_uid
            An identifier used to uniquely identify a particular instance of
            a receiver. This will usually be a string, though it may be
            anything hashable.
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.