FEATURED · 精选文章

基于Python Django的服装仓库进销存系统开发实战

发布时间 / 2026/9/14 11:56:10
来源 / 创域科博编辑部
栏目 / 资讯中心
基于Python Django的服装仓库进销存系统开发实战 简介这是一份基于Python Django框架开发的服装仓库进销存库存管理系统源码包面向毕业设计、课程设计、期末大作业及需要快速搭建管理系统的开发者。系统围绕服装行业实际业务完整覆盖采购入库、销售出库、库存盘点、供应商/客户管理等核心模块并附带数据库脚本与详细文档说明。代码注释清晰项目中关键业务逻辑均有标注即便初学者也能按图索骥理解Django项目分层结构并轻松完成本地部署。压缩包共148个文件以Python源码py/pyc、Django模板html、前端静态资源css/js及数据库文件sqlite3/sql为主整体体积约594KB便于下载、阅读与二次开发。该资源已有398人学习下载内容完整且经过个人高分项目打磨项目逻辑严谨、导师认可度高既适合作为系统学习Django业务开发的实战案例也可直接作为毕业设计或课程设计的高分参考版本。1. 服装仓库进销存为什么偏偏用 PythonDjango 做服装仓库大概是所有进销存场景里最折磨人的一种。同一款T恤白色、黑色、藏青是三个SKU每个SKU下面还有S到XXL五个尺码旺季一天进出十几个款每个款颜色尺码拉出来上百个库存维度换季要处理反季调拨等季末还要面对报损和盘点差异。用Excel管理到这一步基本就崩了。这个标题给的方案是 Python 基于 Django 服装仓库进销存库存管理系统解决的问题非常明确用 Django 的 ORM 和 Admin 把商品、入库、出库、库存这四件事做成能在浏览器里操作的系统数据库用现成的 MySQL 或 SQLite代码和文档结构都齐全适合直接二次开发或者当课程设计、毕设的骨架。适合的人群是有 Python 基础、想把 Django 从Hello World推到真实业务的人。这套东西的价值不在于 Django 有多复杂而在于它的模型设计能直接映射仓库业务。一个服装款式的颜色和尺码在关系型数据库里就是两个字段的组合Django 的模型类写出来之后迁移命令一键建表Admin 后台零成本生成管理界面。这篇文章就顺着模型怎么建、进销存流程怎么写、报表怎么出这个顺序把一份可运行的服装仓库进销存系统从头到尾讲清楚。2. 用 Django 数据模型把服装进销存的实体关系固定下来2.1 服装SKU的粒度颜色和尺码不能塞进一个字符串服装进销存和通用进销存最大的差别在于 SKU 的维度。一个商品是款式层面的概念比如纯棉圆领T恤真正参与库存计算的是纯棉圆领T恤白色L码。如果用 product_name color size 拼成一个字符串当唯一标识后续做库存汇总和报表会非常难受。常见做法是建三张基础表商品表存款式信息颜色表存颜色名称尺码表存尺码的展示值和排序值。然后通过一个中间表把商品、颜色、尺码关联起来形成 SKU 表。Django 里可以用 ForeignKey 串起来也可以把 color 和 size 作为商品表的字段但那样每个颜色尺码组合都要重复存一遍商品信息。我一般会用 SKU 表继承商品信息这样库存、入库、出库都只跟 SKU 打交道不需要任何字符串拼接。# models.py from django.db import models class Product(models.Model): name models.CharField(max_length128, verbose_name款号名称) season models.CharField(max_length16, verbose_name季节, blankTrue) remark models.TextField(verbose_name备注, blankTrue) class Color(models.Model): name models.CharField(max_length32, verbose_name颜色) class Size(models.Model): name models.CharField(max_length16, verbose_name尺码) sort_order models.IntegerField(default0, verbose_name排序) class Sku(models.Model): product models.ForeignKey(Product, on_deletemodels.CASCADE, verbose_name商品) color models.ForeignKey(Color, on_deletemodels.PROTECT, verbose_name颜色) size models.ForeignKey(Size, on_deletemodels.PROTECT, verbose_name尺码) class Meta: unique_together (product, color, size)on_delete 参数里Color 和 Size 用 PROTECT 而不是 CASCADE是因为库存记录还关联着这些数据不允许把有库存的尺码或颜色直接删掉。unique_together 约束了同一个商品下颜色和尺码不能重复建 SKU这是数据层的第一道防线。2.2 入库、出库、库存台账三张表的字段设计进销存业务的核心是单据和台账的关系。入库单记录哪个SKU进了多少件出库单记录哪个SKU出了多少件库存表记录当前还剩多少件。Django 的项目里最常见的设计是单据主表和单据明细表分离主表存单号、日期、经办人明细表存具体 SKU 和数量。库存台账单独建一张表不通过 SUM 临时算这么做有两个好处列表页查库存不用每次聚合几十万条明细入库和出库时能对库存做行级锁避免并发超卖。class StockIn(models.Model): order_no models.CharField(max_length32, uniqueTrue, verbose_name入库单号) create_time models.DateTimeField(auto_now_addTrue, verbose_name入库时间) supplier models.CharField(max_length64, verbose_name供应商, blankTrue) class StockInItem(models.Model): stock_in models.ForeignKey(StockIn, on_deletemodels.CASCADE, verbose_name入库单) sku models.ForeignKey(Sku, on_deletemodels.PROTECT, verbose_nameSKU) quantity models.IntegerField(verbose_name入库数量) class StockOut(models.Model): order_no models.CharField(max_length32, uniqueTrue, verbose_name出库单号) create_time models.DateTimeField(auto_now_addTrue, verbose_name出库时间) customer models.CharField(max_length64, verbose_name客户, blankTrue) class StockOutItem(models.Model): stock_out models.ForeignKey(StockOut, on_deletemodels.CASCADE, verbose_name出库单) sku models.ForeignKey(Sku, on_deletemodels.PROTECT, verbose_nameSKU) quantity models.IntegerField(verbose_name出库数量) class Inventory(models.Model): sku models.OneToOneField(Sku, on_deletemodels.PROTECT, verbose_nameSKU) quantity models.IntegerField(default0, verbose_name当前库存) updated_at models.DateTimeField(auto_nowTrue, verbose_name最后变动时间)StockInItem 和 StockOutItem 的外键字段名要带上 _item 后缀还是用默认的 stockin_id取决于个人习惯但建议在 Meta 里加 db_table 指定表名比如 tb_stock_in_item这样在数据库工具里排查问题更直观。2.3 数据库迁移与初始化数据的两次关键命令模型定义好之后Django 的迁移机制会自动生成建表语句。两个命令把数据库从零建起来python manage.py makemigrations python manage.py migrate如果是全新项目第一次执行之前先确认 INSTALLED_APPS 里已经注册了服装进销存这个 app。makemigrations 会检查模型变化并生成迁移文件migrate 则把迁移文件真正应用到数据库。这一步最常见的报错是数据库连接失败MySQL 的话先确认 settings.py 里的 DATABASES 配置。初始化数据可以用 Django 的 fixture 机制。在 app 的 fixtures 目录下放一个 JSON 文件写入颜色和尺码的基础数据然后执行python manage.py loaddata init_data.json这样不需要在页面里一个个录入尺码表。JSON 里注意 model 字段要写成 app_label.model_name 的格式例如 store.color。执行后可以去 MySQL 里查一下表结构是否和模型对应。3. 用 Django ORM 把入库、出库、盘点三道流程串起来3.1 入库单提交时的库存原子性更新入库流程在视图里只有两步保存入库单主表和明细然后给对应 SKU 的库存加数量。问题是这两步不能分开否则一旦明细保存成功而库存更新失败账就对不上了。Django 的 transaction.atomic 块是标准解法。from django.db import transaction from .models import StockIn, StockInItem, Inventory transaction.atomic def create_stock_in(request): header StockIn.objects.create(order_nogenerate_no(IN), supplier供应商A) for item in request.POST.getlist(items): sku_id item[sku_id] qty int(item[quantity]) StockInItem.objects.create(stock_inheader, sku_idsku_id, quantityqty) inventory, _ Inventory.objects.get_or_create(sku_idsku_id) inventory.quantity inventory.quantity qty inventory.save()get_or_create 返回的是元组第一个值是对象第二个是是否新建的布尔值。入库逻辑里这样写没问题但出库逻辑里不能这么用因为出库要先判断库存够不够get_or_create 在并发下会出现两次都读到同一份旧值的风险。3.2 出库时的库存预扣与并发防线出库要比入库多一步检查库存是否充足。写在应用层的判断在单并发下没问题但两个请求同时读到库存 10各自认为可以出 8最后库存变成 2 而不是 -6这就是超卖。数据库层面的防线有两种。第一种用 Django 的 select_for_update 加行锁第二种用 F 表达式做原子操作。select_for_update 的写法是from django.db import transaction transaction.atomic def create_stock_out(request): for item in request.POST.getlist(items): sku_id item[sku_id] qty int(item[quantity]) inv Inventory.objects.select_for_update().get(sku_idsku_id) if inv.quantity qty: raise ValueError(f{sku_id} 库存不足当前剩余 {inv.quantity}) inv.quantity - qty inv.save()select_for_update 必须在事务里执行它会对命中的行加悲观锁第二个事务要等第一个提交才能读。注意这种方式只对同一行库存的并发有效如果业务允许负库存或者有预占库存的需求锁的策略会更复杂。F 表达式的写法适合不需要前置判断的场景但服装进销存里一般还是先判断再扣减更直观。3.3 盘点差异的三种调整方式仓库盘点之后必然产生差异。差异处理常见做法有三种盘盈盘亏单、直接修改库存、按盘点数覆盖。Django 里可以用一个 Adjustment 模型来记录每次调整的原因和操作人配上 Django admin 自带的历史记录事后能追溯。class InventoryAdjustment(models.Model): sku models.ForeignKey(Sku, on_deletemodels.PROTECT, verbose_nameSKU) before_qty models.IntegerField(verbose_name调整前数量) after_qty models.IntegerField(verbose_name调整后数量) reason models.CharField(max_length255, verbose_name调整原因) created_at models.DateTimeField(auto_now_addTrue)这里注意调整是否也放入事务。调整操作建议和入库出库一样套 atomic因为改库存和写调整单必须一致。还有一点服装行业的盘点通常只盘到 SKU 维度在做盘点单的时候建议先在页面上显示当前库存数再让录入盘点数减少手工填写错误。4. 服装进销存查询与报表从聚合到 Admin 界面优化4.1 用 ORM 聚合统计各 SKU 库存总量进销存系统如果只有流水没有报表运营基本没法用。Django ORM 的 aggregate 和 annotate 是报表统计的两把刀。关键在于搞清楚什么时候用 aggregate什么时候用 annotate。aggregate 返回字典是对整个查询集做汇总annotate 给查询集的每一行增加一个字段。按商品款汇总库存总量可以用 annotatefrom django.db.models import Sum sku_summary Sku.objects.annotate( total_qtySum(inventory__quantity) ).values(product__name, color__name, size__name, total_qty)这段代码里 inventory 是 Sku 模型的反向关系Django 会自动用 Inventory 表做 JOIN。加上 values 之后可以灵活调整分组粒度。要按颜色汇总就把 color__name 加进 values要按整个款汇总就只留 product__name。template 里渲染这个查询结果就能得到类似白色T恤当前共有 X 件的统计表格。4.2 Django Admin 开启进货、出货、库存的可视化管理Django 默认的 admin 够用但界面过于朴素服装进销存里 SKU 数量一多下拉选商品会变得很长不好找。简单优化是在 admin.py 里配置 list_select_related 和 autocomplete_fields减少外键查询次数和选择组件的压力。from django.contrib import admin from .models import Sku, StockInItem admin.register(Sku) class SkuAdmin(admin.ModelAdmin): list_display (product, color, size) list_filter (product__season, color) search_fields (product__name,) admin.register(StockInItem) class StockInItemAdmin(admin.ModelAdmin): autocomplete_fields [sku] list_display (sku, quantity, stock_in)autocomplete_fields 要求目标模型这里就是 Sku注册的 ModelAdmin 里必须写 search_fields否则会报错。这个配置在 SKU 数量超过几百个时体验提升非常明显。4.3 按时间范围筛选出入库记录的 QuerySet 写法运营要看某段时间进了多少货、出了多少货这跟库存汇总不同是带时间条件的过滤。Django 的日期筛选有专门的写法需要注意时区问题。from django.utils import timezone from datetime import timedelta start_date timezone.now() - timedelta(days7) end_date timezone.now() recent_in_orders StockIn.objects.filter(create_time__range(start_date, end_date)) total_in_qty StockInItem.objects.filter( stock_in__create_time__range(start_date, end_date) ).aggregate(totalSum(quantity))这里用了双下划线跨表过滤 stock_in__create_time 而不是直接引用 StockInItem 自己的时间因为明细表里没有时间字段。筛选时间建议用 __range 而不是分别写 __gte 和 __lte代码更紧凑且不会漏边界。需要注意 USE_TZ True 的时候传入的时间对象必须是 aware 类型不能用 naive 的 datetime.now() 直接比较否则会报错或查不到结果。5. 服装仓库进销存的几个必调参数与验证清单5.1 settings.py 里的三个关键配置第一个是数据库连接。MySQL 的话确认 django 版本和 mysqlclient 是否匹配安装 mysqlclient 在 Linux 上需要系统有 mysql-connector-c 的开发包。settings.py 里的 DATABASES 配置中CONN_MAX_AGE 建议设成 60避免每次请求都重新建数据库连接。第二个是语言和时区。默认是 en-us 和 UTC进销存页面要显示中文改成 LANGUAGE_CODE zh-hansTIME_ZONE Asia/Shanghai。改了之后 Django Admin 的界面文字会变成中文。第三个是静态文件。Django 的 debug 模式会自动处理静态文件但部署到生产环境时admin 和自建页面里的 CSS/JS 会 404需要在 settings.py 里配置 STATIC_ROOT 并执行 python manage.py collectstatic。很多人开发环境一切正常用 gunicorn 一跑就没了样式基本都是这一步没做。5.2 用一条命令验证入库出库流程闭环后台管理页面通了最后做一次完整的数据验证。Django shell 里可以模拟一次入库和出库python manage.py shell -c from myapp.models import Sku, Inventory, StockIn, StockInItem from django.db import transaction sku Sku.objects.first() with transaction.atomic(): header StockIn.objects.create(order_noTEST001) StockInItem.objects.create(stock_inheader, skusku, quantity10) inv Inventory.objects.select_for_update().get(skusku) inv.quantity 10 inv.save() print(当前库存:, inv.quantity) 执行后看到库存变为 10 说明入库写库成功。再执行同样的逻辑但把加号改成减号走一遍出库库存回到 0说明整个流程的通路是通畅的。5.3 导出进销存报表时用 StreamingHttpResponse 避免内存爆炸报表功能到了数据量大的时候直接在视图里拼接字符串返回 HttpResponse 会把全量数据都塞进内存。Django 提供 StreamingHttpResponse 适合导出 CSV 这类流式操作。这里只提一个关键点响应对象的 Content-Type 要设置为 text/csv并且带 UTF-8 BOM 头才能在 Excel 里正常打开中文。import csv from django.http import StreamingHttpResponse def export_inventory_csv(request): def generate(): yield \ufeff writer csv.writer(open(/dev/null, w)) writer.writerow([款号, 颜色, 尺码, 库存]) for row in Sku.objects.select_related(product, color, size): inv_qty row.inventory.quantity if hasattr(row, inventory) else 0 yield [row.product.name, row.color.name, row.size.name, inv_qty] response StreamingHttpResponse(generate(), content_typetext/csv; charsetutf-8) response[Content-Disposition] attachment; filenameinventory.csv return responseCSV 生成的 yield 里第一行写入 BOM 头是为了解决 Excel 打开乱码。注意 generator 里不能直接重用同一个 csv.writer而是要自己用 yield 拼接行内容因为 csv.writer 写的是文件句柄。生产环境可以把导出任务丢给 Celery响应里只返回任务 ID前端轮询下载地址这是数据量巨大时的兜底方案。本文还有配套的精品资源点击获取
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻