如何在创建PurchaseItem对象时更新Item模型的inventory字段值?
Here are two reliable methods to automatically update an Item's inventory field whenever a new PurchaseItem is created in Django:
1. Override the save() Method on PurchaseItem
This approach keeps the inventory update logic directly attached to the PurchaseItem model, making it easy to track and maintain.
Modify your PurchaseItem model as follows:
class PurchaseItem(models.Model): product = models.ForeignKey(Item, on_delete=models.CASCADE) quantity = models.PositiveSmallIntegerField() purchase_price = models.DecimalField(max_digits=6, decimal_places=2) paid_amount = models.DecimalField(max_digits=6, decimal_places=2) date_created = models.DateTimeField(auto_now_add=True) def save(self, *args, **kwargs): # Determine if this is a new record (not an update) is_new = self.pk is None # Save the PurchaseItem first to ensure it exists in the database super().save(*args, **kwargs) if is_new: # Increment the related Item's inventory by the purchase quantity self.product.inventory += self.quantity self.product.save()
2. Use a post_save Signal
Signals separate the inventory logic from the model, which is helpful if you need to reuse this functionality or keep your model code focused.
Add this signal handler to your models.py (or a dedicated signals.py file):
from django.db.models.signals import post_save from django.dispatch import receiver @receiver(post_save, sender=PurchaseItem) def update_item_inventory(sender, instance, created, **kwargs): # Only run this logic when a new PurchaseItem is created if created: instance.product.inventory += instance.quantity instance.product.save()
If using a separate signals.py, register it in your app's apps.py:
from django.apps import AppConfig class YourAppConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'your_app_name' def ready(self): import your_app_name.signals
And update __init__.py in your app directory:
default_app_config = 'your_app_name.apps.YourAppConfig'
Key Notes
Atomic Transactions: To prevent partial updates (e.g., PurchaseItem saves but inventory doesn't), wrap the logic in an atomic transaction:
from django.db import transaction # In save() method: with transaction.atomic(): super().save(*args, **kwargs) if is_new: self.product.inventory += self.quantity self.product.save() # In signal handler: @receiver(post_save, sender=PurchaseItem) def update_item_inventory(sender, instance, created, **kwargs): if created: with transaction.atomic(): instance.product.inventory += instance.quantity instance.product.save()Race Condition Prevention: For high-concurrency scenarios, use Django's
F()expressions to let the database handle the increment atomically:from django.db.models import F # Replace the inventory update line with: instance.product.inventory = F('inventory') + instance.quantity instance.product.save()This avoids fetching the current inventory value, modifying it, and saving back—eliminating race conditions where two concurrent updates overwrite each other.
内容的提问来源于stack exchange,提问作者Sesshōmaru

