You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django Admin中save_model函数失效,订单库存校验逻辑未执行求助

问题原因

当你在Admin中使用OrderItemInline时,save_model方法只会先完成Order主对象的保存,此时Inline中的OrderItem还未写入数据库。所以你在save_model里遍历obj.orderitem_set.all()根本拿不到刚添加的订单项,自然库存校验和扣减逻辑完全没执行。

修复方案

改用Django Admin提供的save_formset方法,这个方法专门用于处理Inline表单集的保存流程,能获取到所有待处理的OrderItem数据。同时结合数据库事务,确保一旦校验失败,所有已保存的数据都会回滚,避免数据不一致。

修改后的OrderAdmin代码如下:

from django.contrib import admin
from django.contrib import messages
from django.utils.translation import ngettext
from .models import Category, Product, Order, OrderItem
from django.utils.html import format_html
from django.db import transaction  # 导入事务


class OrderItemInline(admin.TabularInline):
    model = OrderItem
    extra = 1

class OrderAdmin(admin.ModelAdmin):
    inlines = [OrderItemInline]
    list_display = ('id', 'user', 'order_date')

    @transaction.atomic  # 开启原子事务,出错自动回滚
    def save_formset(self, request, form, formset, change):
        errors = []
        # 先获取所有待保存的OrderItem对象(不立即写入数据库)
        order_items = formset.save(commit=False)
        
        # 批量做库存校验
        for order_item in order_items:
            if order_item.quantity > order_item.product.quantity:
                errors.append(
                    f"商品【{order_item.product.name}】订购数量({order_item.quantity})超过库存({order_item.product.quantity})"
                )
        
        if errors:
            # 有错误就提示,终止保存流程
            message = ngettext(
                "订单未保存,原因:",
                "订单未保存,原因:",
                len(errors),
            )
            messages.error(request, f"{message} {';'.join(errors)}")
            return
        
        # 校验通过,先保存订单项到数据库
        formset.save()
        # 批量扣减库存
        for order_item in order_items:
            order_item.product.quantity -= order_item.quantity
            order_item.product.save()
        
        # 执行父类的保存逻辑
        super().save_formset(request, form, formset, change)

# 其他Admin注册代码保持不变
admin.site.register(Category)
class ProductAdmin(admin.ModelAdmin):
    list_display = ['display_image', 'name', 'quantity', 'price', 'category']

    def display_image(self, obj):
        if obj.image:
            return format_html('<img src="{}" width="50" height="50" />'.format(obj.image.url))
        return "无图片"

    display_image.short_description = '图片'
admin.site.register(Product,ProductAdmin)
admin.site.register(Order, OrderAdmin) 
额外说明
  • @transaction.atomic装饰器确保整个保存流程是原子性的,只要有一个订单项校验失败,所有已执行的数据库操作都会回滚,避免出现订单保存但库存未扣减、或库存扣减但订单未保存的不一致情况。
  • formset.save(commit=False)生成OrderItem对象但不立即写入数据库,方便我们先完成全量校验,再统一执行保存操作。
  • 先收集所有错误再统一提示,能让用户一次性看到所有问题,提升操作体验。

内容的提问来源于stack exchange,提问作者Praveen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 23:25:26