Django:如何在模型方法中跨模型取数并在Admin展示库存
Django Admin 实现跨模型库存汇总表格方案
嘿,作为Django新手能自己摸索到Admin定制这一步已经很棒啦!针对你想要在Admin里新增库存汇总表格的需求,其实不用特意创建一个空模型——这里有两种实用的方案,你可以根据自己的需求选择:
方案一:扩展现有GoodsItem的Admin页面(最简单)
既然你已经在Admin里注册了GoodsItem,直接给它的Admin配置添加一个自定义字段来展示可用库存就好,不用额外创建新模型。
步骤:修改你的admin.py
from django.contrib import admin from .models import GoodsItem, FinishedGoodsItem, SoldGoodsItem class GoodsItemAdmin(admin.ModelAdmin): # 把自定义的可用库存字段加入展示列表 list_display = ('__str__', 'available_stock') def available_stock(self, obj): # 计算当前GoodsItem对应的所有Finished记录的总重量 # 用aggregate求和,没有记录时返回0避免报错 finished_total = obj.finished_name.aggregate(total=models.Sum('weight'))['total'] or 0 # 计算当前GoodsItem对应的所有Sold记录的总重量 sold_total = obj.sold_name.aggregate(total=models.Sum('weight'))['total'] or 0 # 返回可用库存:完成总量 - 销售总量 return finished_total - sold_total # 设置表格列的显示标题 available_stock.short_description = '可用库存' # 如果需要支持排序,可以加上这行(方案二排序更高效) # available_stock.admin_order_field = 'available_stock' # 注册时绑定自定义的Admin类 admin.site.register(GoodsItem, GoodsItemAdmin) admin.site.register(FinishedGoodsItem) admin.site.register(SoldGoodsItem)
关键说明:
obj.finished_name是你在FinishedGoodsItem里设置的related_name="finished_name",通过它可以直接反向查询当前GoodsItem对应的所有完成记录aggregate是Django ORM的聚合函数,用来批量计算总和,or 0是为了处理没有对应记录的情况(避免返回None导致减法报错)
方案二:创建独立的库存汇总Admin页面(更清晰)
如果你想要一个单独的Admin页面展示汇总数据,不和GoodsItem的列表混在一起,可以创建一个虚拟模型(不生成数据库表),然后绑定自定义的Admin逻辑。
步骤1:在models.py里添加虚拟模型
from django.db import models class StockSummary(models.Model): class Meta: managed = False # 告诉Django不要为这个模型创建数据库表 verbose_name = '库存汇总表' verbose_name_plural = '库存汇总表'
步骤2:在admin.py里注册并配置这个模型
from django.contrib import admin from django.db.models import Sum, F from .models import StockSummary, GoodsItem class StockSummaryAdmin(admin.ModelAdmin): list_display = ('goods_item', 'available_stock') # 禁止添加、修改、删除操作(因为这是只读统计) actions = None def has_add_permission(self, request): return False def has_change_permission(self, request, obj=None): return False def has_delete_permission(self, request, obj=None): return False def get_queryset(self, request): # 用annotate批量计算每个GoodsItem的库存,性能更优(避免N+1查询) return GoodsItem.objects.annotate( # 计算完成总重量,没有记录时默认0 finished_total=Sum('finished_name__weight', default=0), # 计算销售总重量,没有记录时默认0 sold_total=Sum('sold_name__weight', default=0) ).annotate( # 用F表达式直接在数据库层面计算差值 available_stock=F('finished_total') - F('sold_total') ) def goods_item(self, obj): # 展示GoodsItem的字符串表示 return str(obj) goods_item.short_description = 'GoodsItem' admin.site.register(StockSummary, StockSummaryAdmin)
关键说明:
managed=False让Django不为这个模型创建数据库表,只是用来给Admin做展示载体annotate是批量给每个GoodsItem添加统计字段,比方案一的每条单独查询更高效,数据量大时差异明显F表达式用来直接在数据库层面计算差值,避免把数据拉到Python层面再计算,性能更好
额外提示(按日期过滤需求)
如果你需要按“截至最后一条记录日期”来计算(而不是所有记录),可以在聚合时添加过滤条件,比如:
from django.db.models import Q # 假设要取每个GoodsItem对应的Finished记录中最新日期的所有记录总和 finished_latest_date = obj.finished_name.aggregate(max_date=models.Max('date'))['max_date'] finished_total = obj.finished_name.filter(date=finished_latest_date).aggregate(total=models.Sum('weight'))['total'] or 0
你可以根据自己的具体逻辑调整这个过滤条件。
内容的提问来源于stack exchange,提问作者rmbits
相关产品推荐
相关产品推荐

