Django post_save () Signalimplementierung

72

Ich habe eine Frage zu Django.

Ich habe ManyToMany Modelle hier

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)

Für 1 erstelltes Warenkorbobjekt kann ich so viele wie neue TransactionDetail-Objekte (das Produkt und die Menge) einfügen. Meine Frage ist. Wie kann ich den Trigger implementieren? Ich möchte, dass bei jeder Erstellung eines Transaktionsdetails der Betrag des Produktbestands durch den Betrag im Transaktionsdetail abgezogen wird.

Ich habe über post_save () gelesen, bin mir aber nicht sicher, wie ich es implementieren soll. vielleicht so etwas

wann:

post_save(TransactionDetail, 
       Cart) #Cart object where TransactionDetail.cart= Cart.id
Cart.stock -= TransactionDetail.amount
Haris Hamdani
quelle
5
Wenn du das so machst, wirst du wahrscheinlich auf Rennbedingungen stoßen.
Thomas Orozco

Antworten:

165

Wenn Sie wirklich Signale verwenden möchten, um dies zu erreichen, erfahren Sie hier kurz, wie:

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()
Kenny Shen
quelle
1
Dies funktioniert gut für mich, aber ich weiß nicht, warum es in einer Schleife von unbekannter Länge ist
Manoj Sahu
6
Ich erhalte eine maximum recursion depth exceededFehlermeldung, weil ich die Instanz selbst in der @receiverFunktion gespeichert habe. Wie kann ich Selbstmodelle aktualisieren? Muss ich die Modellmethode überschreiben save()?
Dipak
1
Die maximale Rekursionstiefe von @Dipak wurde überschritten, da bei jedem Aktualisieren der Instanz post_save ausgelöst wird und sich bei jedem Speichern selbst aufruft. In einem Zustand, in dem der Wert für die Rekursionstiefe die Ausnahme überschreitet, tritt dies auf. Lassen Sie mich wissen, wie Sie das überwunden haben
Vamsidhar Muggulla
3
@VamsidharMuggulla Anstatt signalI zu verwenden, überschreibe ich die saveModellmethode und verwende die updatedfunktionsaktualisierte Modelleigenschaft, damit das Speichern nicht erneut ausgelöst wird.
Dipak
6
Wofür ist dispatch_uid?
R11G
18

Persönlich würde ich die save () -Methode von TransactionDetail überschreiben und dort das neue TransactionDetail speichern und dann ausführen

self.product.stock -= self.amount
self.product.save()
Mikael
quelle
13

Wenn Sie das Abrufen vermeiden möchten, maximum recursion depth exceededsollten Sie die Signale trennen , bevor Sie sie im Signalhandler speichern. Das obige Beispiel (Kenny Shens Antwort) wäre dann:

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)

Dies wird ausführlich unter Trennen von Signalen für Modelle und erneutes Verbinden in Django mit einem abstrakteren und nützlicheren Beispiel beschrieben.

Siehe auch: https://docs.djangoproject.com/de/2.0/topics/signals/#disconnecting-signals in den Django-Dokumenten.

Guglielmo Celata
quelle
Damit dies funktioniert, müssen Sie dispatch_uiddie Methoden disconnectund haben connect. Die Rückgabewerte helfen Ihnen beim Umgang mit potenziellen Fehlern. `Is_disconnected = post_save.disconnect (update_stock, sender = TransactionDetail, dispatch_uid = "update_stock_count") , wenn is_diconnected: instance.product.save () is_reconnected = post_save.connect (update_stock, sender = TransactionDetail, dispatch_uid = "update_stock_count")`
califlower
2

Wenn Sie wirklich Signale in Django verwenden möchten, versuchen Sie dies bitte:

#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

Fügen Sie dann default_app_config in die Init-Datei ein

 default_app_config = "give your AppConfig path"
a.patidar
quelle
2

In der Tat erklärt der Dokumentstring das Signalsist in 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.
Zulu-
quelle