Reputation: 1624
I have a model in django, let's call it product
, that is referenced by multiple purchase
instances.
A purchase can be made for any number of items of the product
, subject to the following constraint:
The total of all items in all purchases for a given product must be less than a set maximum number of items, which is defined differently for each product.
Users may create a purchase
for a product, and I want to keep track of the total number of items covered by all purchases at any given time.
This is complicated by the fact that users can modify or delete their purchase
, thereby changing the total number of items purchased.
How would I go about keeping track of this number for each product
and updating it every time a purchase
is changed? Is there a hook that can listen to the purchases for a product
and detect a change?
purchase
model:
class Purchase(models.Model):
items = models.IntegerField(blank=False, default=1)
delivery_method = models.CharField(max_length=100, blank=False, default='')
#...
product = models.ForeignKey('product.Product',
related_name='purchases', on_delete=models.CASCADE)
product
model:
class Product(models.Model):
name = models.CharField(max_length=100, blank=False,)
items_offered = models.IntegerField(blank=False, default=2)
# Items purchased should be the total number
# of items in all purchases for this product...
# How do I keep it updated?
items_purchased = models.IntegerField(blank=False, default=0)
Upvotes: 0
Views: 379
Reputation:
Simple way is override save, delete methods, or use django signals:
class Purchase(models.Model):
# ... model definition
def update_items_purchased(self, product):
purchases = Purchase.objects.filter(product=product)
if purchases:
total = purchases.aggregate(total=Sum('items')).get('total', 0)
else:
total = 0
product.items_purchased = total
product.save()
def save(self, *args, **kwargs):
super(Purchase, self).save(*args, **kwargs)
self.update_items_purchased(self.product)
def delete(self, *args, **kwargs):
super(Purchase, self).delete(*args, **kwargs)
self.update_items_purchased(self.product)
Upvotes: 1
Reputation: 5193
Use a post_save
signal.
from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender=Purchase)
def update_purchase_amounts(sender, instance, created, **kwargs):
product = Product.objects.get(id=instance.product.id)
product.items_purchased += instance.items
product.save(update_fields=['items_purchased'])
I'm assuming purchase.items
is the number of product
in the Purchase
.
Though, you may want to do it differently like aggregate all Purchase.items
fields for the product so that you don't keep updating the amount purchased every time you save a Purchase
instance. So maybe use something like:
from django.db.models.aggregates import Sum
counts = Purchase.objects.filter(product=instance.id).aggregate(Sum('items'))
product.items_purchased = counts
product.save(update_fields=['items_purchased'])
Upvotes: 1