如何在Django中实现跨表IntegerField数值扣减(花束场景)
需求说明
工作机制
向Bouquet表添加产品时,在CompositionOfTheBouquetInline类中可从Flower表选择花卉对象并指定数量。
示例
- Flower表中存在“Rose”,stock = 10
- Bouquet表中创建“Bouquet of roses”花束,stock = 1
- 在CompositionOfTheBouquet表中选择5朵“Rose”并保存
预期结果
- Flower表中“Rose”的stock 变为5
计算公式
Flower.stock = Flower.stock - (CompositionOfTheBouquet.count * Bouquet.stock)
现有代码
models.py
class Flower(models.Model): title = models.CharField(max_length=100) stock = models.PositiveIntegerField(default=0) class Bouquet(models.Model): title = models.CharField(max_length=150) stock = models.PositiveIntegerField(default=0) class CompositionOfTheBouquet(models.Model): flower = models.ForeignKey( Flower, on_delete=models.PROTECT ) bouquet = models.ForeignKey( Bouquet, on_delete=models.PROTECT ) count = models.PositiveIntegerField(default=0)
admin.py
from .models import Flower, Bouquet, CompositionOfTheBouquet class CompositionOfTheBouquetInline(admin.TabularInline): model = CompositionOfTheBouquet @admin.register(Flower) class Flower(admin.ModelAdmin): pass @admin.register(Bouquet) class Bouquet(admin.ModelAdmin): inlines = [CompositionOfTheBouquetInline, ]
核心需求
通过admin面板添加Bouquet时,需按照公式自动扣减对应花卉的stock字段值并保存到Flower表。
实现方案
要搞定这个自动扣减库存的需求,我们可以重写Bouquet对应的ModelAdmin中的save_related方法——这个方法会在保存花束主对象及其关联的花卉组成条目之后触发,刚好适合处理关联的库存更新逻辑。
修改后的admin.py代码如下:
from django.contrib import admin from django.db.models import F from .models import Flower, Bouquet, CompositionOfTheBouquet class CompositionOfTheBouquetInline(admin.TabularInline): model = CompositionOfTheBouquet @admin.register(Flower) class FlowerAdmin(admin.ModelAdmin): pass @admin.register(Bouquet) class BouquetAdmin(admin.ModelAdmin): inlines = [CompositionOfTheBouquetInline, ] def save_related(self, request, form, formsets, change): # 先调用父类方法保存所有关联的花卉组成条目 super().save_related(request, form, formsets, change) bouquet = form.instance # 获取当前花束对应的所有花卉组成 compositions = CompositionOfTheBouquet.objects.filter(bouquet=bouquet) for comp in compositions: # 计算需要扣减的库存数量 deduct_num = comp.count * bouquet.stock # 使用F表达式直接在数据库层面更新,避免并发冲突 Flower.objects.filter(id=comp.flower.id).update( stock=F('stock') - deduct_num )
补充说明
- 选用
save_related而非save_model,是为了确保所有inline的花卉组成条目都已保存到数据库,才能获取完整的组成数据计算库存扣减。 - 使用
F表达式更新库存,是为了避免多用户同时操作时出现的库存数据不一致问题,直接让数据库完成数值计算,比先取数据再计算再保存更安全。 - 如果需要支持编辑花束时的库存调整(比如修改花束库存数量、更换花卉或调整数量),还需要额外记录编辑前的旧数据,先反向回滚库存再重新计算扣减,上述代码仅处理新增花束的场景。
内容的提问来源于stack exchange,提问作者Maxim Odintsov
相关产品推荐
相关产品推荐

