Reputation: 33
I am hoping someone can explain to me why post_save is not working when post_delete seemingly is and they're very similar and in the same signals.py file:
Django version 3+ Python version 3+
models.py:
import uuid
from django.db import models
from django.db.models import Sum
from django.conf import settings
from products.models import Product
from shows.models import ShowsTickets
class Order(models.Model):
order_number = models.CharField(max_length=32, null=False, editable=False)
full_name = models.CharField(max_length=50, null=False, blank=False)
email = models.EmailField(max_length=254, null=False, blank=False)
phone_number = models.CharField(max_length=20, null=False, blank=False)
country = models.CharField(max_length=40, null=False, blank=False)
postcode = models.CharField(max_length=20, null=True, blank=True)
town_or_city = models.CharField(max_length=40, null=False, blank=False)
street_address1 = models.CharField(max_length=80, null=False, blank=False)
street_address2 = models.CharField(max_length=80, null=True, blank=True)
county = models.CharField(max_length=40, null=True, blank=True)
date = models.DateTimeField(auto_now_add=True)
delivery_cost = models.DecimalField(max_digits=6, decimal_places=2, null=False, default=0)
products_total = models.DecimalField(max_digits=6, decimal_places=2, null=True, default=0)
tickets_total = models.DecimalField(max_digits=6, decimal_places=2, null=True, default=0)
order_total = models.DecimalField(max_digits=6, decimal_places=2, null=False, default=0)
grand_total = models.DecimalField(max_digits=6, decimal_places=2, null=False, default=0)
def _generate_order_number(self):
"""Generate a random, unique order number using UUID"""
return uuid.uuid4().hex.upper()
def update_total(self):
"""Update grand total each time a line item is added,
accounting for delivery costs.
"""
self.products_total = (self.productlineitems.aggregate(
Sum('productlineitems_total'))['productlineitems_total__sum']) or 0
self.tickets_total = (self.ticketlineitems.aggregate(
Sum('ticketlineitems_total'))['ticketlineitems_total__sum']) or 0
self.order_total = self.products_total + self.tickets_total
self.delivery_cost = settings.STANDARD_DELIVERY_CHARGE
self.grand_total = self.order_total + self.delivery_cost
self.save()
def save(self, *args, **kwargs):
"""Override the original save method to set the order number
if it hasn't been set already.
"""
if not self.order_number:
self.order_number = self._generate_order_number()
super().save(*args, **kwargs)
def __str__(self):
return self.order_number
class OrderLineProductItem(models.Model):
order = models.ForeignKey(Order, null=False, blank=False, on_delete=models.CASCADE, related_name='productlineitems')
product = models.ForeignKey(Product, null=False, blank=False, on_delete=models.CASCADE)
quantity = models.IntegerField(null=False, blank=False, default=0)
productlineitems_total = models.DecimalField(max_digits=6, decimal_places=2, null=False, blank=False, editable=False)
def save(self, *args, **kwargs):
"""Override the original save method to set the lineproductitem total
and update the order total.
"""
self.productlineitems_total = self.product.price * self.quantity
super().save(*args, **kwargs)
def __str__(self):
return f'{self.product} on order {self.order.order_number}'
class OrderLineTicketItem(models.Model):
order = models.ForeignKey(Order, null=False, blank=False, on_delete=models.CASCADE, related_name='ticketlineitems')
ticket = models.ForeignKey(ShowsTickets, null=False, blank=False, on_delete=models.CASCADE)
quantity = models.IntegerField(null=False, blank=False, default=0)
ticketlineitems_total = models.DecimalField(max_digits=6, decimal_places=2, null=False, blank=False, editable=False)
def save(self, *args, **kwargs):
"""Override the original save method to set the lineticketitem total
and update the order total.
"""
self.ticketlineitems_total = self.ticket.price * self.quantity
super().save(*args, **kwargs)
def __str__(self):
return f'{self.ticket} on order {self.order.order_number}'
signals.py:
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from .models import OrderLineProductItem, OrderLineTicketItem
# not being called on update/create
@receiver(post_save, sender=OrderLineTicketItem)
@receiver(post_save, sender=OrderLineProductItem)
def update_on_save(sender, instance, created, **kwargs):
"""Update order total on lineproductitem and
lineticketitem update/create
"""
print('Hello, signals here!')
instance.order.update_total()
# works fine and, when called, updates everything accordingly
@receiver(post_delete, sender=OrderLineTicketItem)
@receiver(post_delete, sender=OrderLineProductItem)
def update_on_save(sender, instance, **kwargs):
"""Update order total on lineproductitem and
lineticketitem delete"""
print('Hello, signals here!')
instance.order.update_total()
I have also added import signals
to the app's config class' ready()
method.
The idea is for the signals to update the Order's product_total, ticket_total, and order_total fields through the update_total()
method.
When creating an instance, or updating one, with either item, the totals are not calculated. However, when an instance is deleted, everything works as expected and the post_delete signal fires (I tested this with a print() function as you can see above.)
Why is the post_save part not firing at all (upon create or update)?
Upvotes: 0
Views: 1495
Reputation: 33
OK, so I have fixed the issue. It turned out to be a rookie oversight on my part.
My signal functions had the same name. Changing the @post_delete function to update_on_delete() - instead of update_on_save() - did the trick.
Upvotes: 1