Django post_save () implementación de señal

Tengo una pregunta sobre django.

Tengo muchos modelos de muchos aquí

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) 

Para 1 objeto de carrito creado, puedo insertar tantos como nuevo objeto TransactionDetail (el producto y la cantidad). Mi pregunta es. ¿Cómo puedo implementar el disparador? Lo que quiero es que cada vez que se crea un detalle de Transacción, quiero que la cantidad del stock del producto se resta por la cantidad en la transacción detallada.

He leído sobre post_save () pero no estoy seguro de cómo implementarlo. tal vez algo como esto

when: post_save (TransactionDetail, Cart) #Cart objeto donde TransactionDetail.cart = Cart.id

 Cart.stock -= TransactionDetail.amount 

Si realmente desea utilizar señales para lograr esto, aquí hay brevemente cómo,

 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 instance.product.save() 

Personalmente, yo anularía el método save () de TransactionDetail y allí guardaré el nuevo TransactionDetail y luego ejecutaré

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

Si desea evitar que se maximum recursion depth exceeded , debe desconectar las señales antes de guardarlas en el controlador de señales. El ejemplo anterior (respuesta de Kenny Shen), sería entonces:

 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) 

Esto se describe detalladamente en Señales de desconexión para modelos y reconectarse en django , con un ejemplo más abstracto y útil.

También vea: https://docs.djangoproject.com/en/2.0/topics/signals/#disconnecting-signals en los documentos de django.

En hechos, el documento que explica las Signals está en django.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.