Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I update a model field based on updating another model's field using Signals?

Tags:

django

signals

I am trying to add all the total values in Transaction model and put them in the Sale model's first instance (pk=1) gross_total field. Here is my code.

models.py

class Sale(models.Model):
    gross_total = models.FloatField()

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

class Transaction(models.Model):
    sale = models.ForeignKey('Sale')
    price = models.FloatField()
    quantity = models.IntegerField()
    total = models.FloatField(blank=True, null=True)

    def save(self):
        self.total = self.price * self.quantity
        return super(Transaction, self).save()

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

signals.py

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

from .models import Transaction, Sale

@receiver(post_save, sender=Transaction)
def update_sale(sender, **kwargs):
    sale = Sale.objects.get(pk=1)
    sale.gross_total = Transaction.objects.all().aggregate(Sum('total'))['total__sum']
    sale.save()

I am new to using Django Signals. What is it that I am doing wrong? If I save an instance of Transaction model the Sale model data doesn't get updated!

like image 788
MiniGunnR Avatar asked Sep 15 '25 02:09

MiniGunnR


1 Answers

What happens is...

When you write code in signals.py it's a valid code, but it's never run, untill this module is executed directly (not intended use though in this case) or imported. So it must be imported somewhere in order for code to be run and handler functions be registered to corresponding signals.

When your Django project is ran, Django creates application registry from apps declared in your settings.py. While creating a registry it imports every package and module declared in settings.pyand also calls several predefined functions during this process including ready() inside every AppConfig.

When importing a package in Python the code inside special file __init__.py is executed automatically by Python internals, usually it allows to define special behavior and properties for a package.

So basicly, this leads to:

  1. Django project is launched.

  2. Django reads settings.py and finds your App as one of declared ones.

  3. Django imports your App package, the code inside __init__.py is ran and default_app_config is specified. Now Dgango knows it.

  4. When processing of all apps has finished, Django calls ready() function for every AppConfig found during previous steps.

  5. ready() in MyAppConfig is run and your signals.py module gets imported.

  6. During this import, the code inside signals.py is executed: your update_sale function is created and @receiver decorator is executed with it as argument.

  7. The receiver decorator code while being executed registers update_sale function to be run on post_save signal emitted by Transaction model class.

like image 107
Nikita Avatar answered Sep 16 '25 15:09

Nikita