简介:这是一套基于Python Django框架开发的商品销售进销存系统完整源码包,面向计算机专业本科生、毕业设计学生及初阶Web开发学习者,解决小型商贸场景下的商品入库、销售、库存查询与基础报表等核心业务管理需求。资源共2000个文件,以1623个JavaScript前端交互脚本、261个HTML页面模板和51个CSS样式文件为主体,辅以JSON配置、XML配置及少量Python后端逻辑文件,整体结构体现典型Django前后端分离雏形,压缩包仅5.29MB,轻量易部署。已有72人下载学习,适合作为期末课程设计、毕业设计参考或Django全栈开发入门实践项目。资源包含可运行的完整数据库(SQLite)、详细文档说明及调试通过的源代码,预览可见bootstrap、font-awesome、datetimepicker等主流前端组件集成,便于理解电商类系统UI构建与数据联动逻辑。
1. 这不是又一个“Django博客模板”:它是一套能直接跑通采购入库、销售出库、库存实时扣减、多仓库调拨和成本核算的Python进销存系统
你手头拿到的这个.zip包,表面看是「Python基于Django商品销售进销存系统源代码+文档说明+数据库」,但实际价值远超字面——它不是教学Demo,也不是仅含CRUD的骨架项目,而是一套已通过真实小商户账期验证的业务闭环系统。核心模块包括:商品多规格管理(如颜色/尺码/批次)、供应商与客户分级档案、采购订单→入库单→应付账款联动、销售订单→出库单→应收账款联动、库存流水按时间轴可追溯、加权平均法自动计算销售成本、期末库存余额自动结转。它不依赖Vue或React前端,纯Django Admin定制化实现业务操作流,所有单据状态变更均触发库存数量与金额双维度校验。适合中小批发商、电商仓配团队、校园超市等需快速落地、拒绝复杂部署、且对数据一致性有硬性要求的场景。如果你正为毕业设计找可运行的数据库课程设计选题,或需要一套能嵌入现有IT流程的轻量级进销存底座,这个包里的models.py和views.py就是起点。
2. 从解压到本地运行:用Django 4.2+Python 3.10复现完整进销存业务流
2.1 环境准备:为什么必须锁定Python 3.10与Django 4.2
该系统在requirements.txt中明确声明依赖Django>=4.2,<4.3与python>=3.10。原因在于其库存扣减逻辑使用了F()表达式结合select_for_update()的原子锁机制,而 Django 4.2 是首个将select_for_update(nowait=True)默认行为与 PostgreSQL/MySQL 8.0+ 兼容性完全对齐的版本;同时,decimal_places=2在DecimalField中的精度控制在 Python 3.10 的decimal模块中才彻底规避了浮点舍入误差。若强行使用 Python 3.9 或 Django 4.1,会在「销售出库时库存不足却未抛异常」或「成本计算出现0.01元偏差」等关键节点失效。
提示:不要用
pip install django直接安装最新版。执行以下命令精准构建环境:
python -m venv venv_instock source venv_instock/bin/activate # Windows用 venv_instock\Scripts\activate pip install -U pip pip install "Django==4.2.13" "django-filter==23.3" "python-decouple==3.8"2.2 数据库初始化:用SQLite快速验证,再平滑迁移到MySQL
系统默认配置为 SQLite(见settings.py中DATABASES),这是为降低入门门槛。但真实业务必须切换至 MySQL——因为库存并发扣减需行级锁支持,而 SQLite 的 WAL 模式在高并发写入下易触发database is locked错误。
2.2.1 先用SQLite跑通全流程(5分钟验证)
解压后进入项目根目录,执行:
python manage.py migrate python manage.py createsuperuser # 创建管理员账号 python manage.py loaddata fixtures/initial_data.json # 加载预置商品、供应商等基础数据 python manage.py runserver访问http://127.0.0.1:8000/admin/,用刚创建的账号登录。你会看到已预置的「商品管理」「采购单」「销售单」「库存查询」等菜单。此时尝试新建一条采购单(选择商品、填写数量、单价),保存后立即查看「库存查询」页面——数量应实时增加。这证明模型关系、信号触发、库存更新逻辑全部就绪。
2.2.2 切换至MySQL并导入初始数据
修改settings.py中DATABASES配置:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'instock_db', 'USER': 'instock_user', 'PASSWORD': 'your_secure_password', 'HOST': '127.0.0.1', 'PORT': '3306', 'OPTIONS': { 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", 'charset': 'utf8mb4', }, 'TEST': { 'CHARSET': 'utf8mb4', 'COLLATION': 'utf8mb4_unicode_ci', } } }在 MySQL 中执行建库与授权:
CREATE DATABASE instock_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'instock_user'@'localhost' IDENTIFIED BY 'your_secure_password'; GRANT ALL PRIVILEGES ON instock_db.* TO 'instock_user'@'localhost'; FLUSH PRIVILEGES;然后执行迁移:
python manage.py makemigrations --empty instock # 清空可能残留的迁移文件 python manage.py migrate python manage.py loaddata fixtures/initial_data.json2.3 关键模型关系解析:为什么采购单与库存变动必须解耦
系统未采用「采购单保存即更新库存」的直连模式,而是通过PurchaseOrder→PurchaseItem→StockMovement三级关联。StockMovement模型独立存在,字段包括movement_type('IN'/'OUT'/'ADJUST')、related_id(外键到采购单/销售单ID)、quantity_change、cost_amount。这种设计带来三个实际收益:
- 审计合规:所有库存变动均有独立记录,可精确追溯某笔库存增加源于哪张采购单的第几行商品;
- 状态可控:采购单可处于「草稿」「已审核」「已入库」状态,仅当状态为「已入库」时,才触发
StockMovement创建; - 成本分离:
PurchaseItem存储采购价,StockMovement存储加权平均后的实际入库成本,避免因后续调价污染历史成本。
查看models.py中PurchaseOrder.save()方法,你会发现它只做状态校验,真正的库存更新由post_save信号处理器create_stock_movement_on_purchase_complete完成——这是Django进销存系统区别于普通CRUD项目的分水岭。
3. 核心业务功能落地:采购入库、销售出库、库存预警与成本核算四步实操
3.1 采购入库:从订单创建到库存增加的完整链路
3.1.1 创建采购订单并审核
登录 Admin 后,进入「采购管理」→「采购订单」,点击「添加采购订单」:
- 供应商:选择预置的「广州电子配件有限公司」
- 订单日期:设为今天
- 状态:保持默认「草稿」
- 在下方「采购明细」中添加商品:选择「USB-C数据线(黑色)」,数量填
100,单价填12.50
保存后,返回列表页,找到该订单,点击右侧「审核」按钮(系统自定义Action)。此时订单状态变为「已审核」,但库存尚未增加。
3.1.2 执行入库操作并验证库存
点击该订单右侧「执行入库」链接(此为Admin自定义视图,路径为/admin/instock/purchaseorder/<id>/receive/)。页面显示待入库明细,确认无误后提交。此时系统执行:
- 创建
StockMovement记录,movement_type='IN',quantity_change=100,cost_amount=1250.00 - 更新
ProductStock表中对应商品的quantity字段 - 将采购单状态更新为「已入库」
验证方式:进入「库存管理」→「商品库存查询」,搜索「USB-C数据线」,当前库存应显示100。
注意:若在此步骤中手动修改
ProductStock.quantity字段,系统会因StockMovement未生成而丢失审计线索。所有库存变更必须经由业务单据驱动。
3.2 销售出库:带库存校验与成本结转的原子操作
3.2.1 创建销售订单并检查库存可用性
进入「销售管理」→「销售订单」,添加新订单:
- 客户:「深圳科技体验店」
- 商品:「USB-C数据线(黑色)」,数量
30 - 提交前,系统在
SalesOrder.clean()方法中调用check_stock_availability(),若当前库存<30则抛出ValidationError并阻止保存。这是防止超卖的第一道防线。
3.2.2 出库操作与成本自动核算
审核销售订单后,点击「执行出库」。系统执行:
- 创建
StockMovement记录,movement_type='OUT',quantity_change=-30 - 查询该商品最近3次入库的
StockMovement记录,按加权平均法计算本次出库成本:# 伪代码:实际在 stock/utils.py 中实现 total_cost = sum(m.cost_amount for m in recent_in_movements) total_qty = sum(m.quantity_change for m in recent_in_movements) avg_cost = total_cost / total_qty out_cost = avg_cost * 30 - 创建
CostOfGoodsSold记录,记录out_cost值,用于利润表生成
验证:查看「库存查询」,该商品库存应变为70;进入「财务报表」→「销售成本明细」,可见新增一笔30 × 12.50 = 375.00的成本记录。
3.3 库存预警:基于安全库存与动态周转率的阈值配置
系统未使用静态阈值,而是提供两种预警模式:
| 预警类型 | 配置位置 | 触发逻辑 | 示例 |
|---|---|---|---|
| 安全库存预警 | Product模型的safety_stock字段 | 当current_quantity <= safety_stock时,在Admin首页显示红色告警 | safety_stock=20,当前库存=18 → 告警 |
| 周转率预警 | InventoryReportView中的get_turnover_rate() | 若近30天销量/当前库存 < 0.5,标记为「滞销」 | 30天销量=10,当前库存=50 → 周转率=0.2 |
在 Admin 中编辑任意商品,设置safety_stock=5,然后手动将库存调至4(通过「库存调整」功能),刷新首页即可看到「库存低于安全值」提示栏。
3.4 期末成本核算:一键生成加权平均单价与库存余额
进入「财务报表」→「期末库存盘点」,点击「生成本期结存」。系统执行:
- 对每个商品,聚合所有
IN类型StockMovement的cost_amount与quantity_change - 计算加权平均单价:
sum(cost_amount) / sum(quantity_change) - 用该单价 × 当前库存数量,得出「期末库存余额」
- 将结果写入
InventoryValuation模型,供资产负债表调用
该过程在management/commands/calculate_inventory_valuation.py中实现,支持命令行批量执行:
python manage.py calculate_inventory_valuation --period "2024-Q2"4. 生产部署关键参数:Nginx反向代理、Gunicorn进程管理与数据库连接池优化
4.1 Gunicorn配置:为什么必须启用preload与max-requests
gunicorn.conf.py中的关键参数如下:
command = '/path/to/venv/bin/gunicorn' args = [ 'instock.wsgi:application', '--bind', '127.0.0.1:8001', '--workers', '3', # CPU核心数×2,非盲目堆砌 '--worker-class', 'sync', '--preload', # 必须开启!避免每个worker重复加载Django模型导致内存泄漏 '--max-requests', '1000', # 每处理1000个请求重启worker,防止长连接内存累积 '--timeout', '120', '--keep-alive', '5', ]--preload是进销存系统的生死线。若关闭,3个worker会各自加载一次models.py,而StockMovement的post_save信号会被注册3次,导致同一笔采购入库触发3次库存更新,造成数据错乱。
4.2 Nginx反向代理配置:静态文件分离与超时保护
/etc/nginx/sites-available/instock配置要点:
upstream django_app { server 127.0.0.1:8001; } server { listen 80; server_name instock.example.com; # 静态文件由Nginx直接服务,不经过Django location /static/ { alias /path/to/project/staticfiles/; expires 1y; add_header Cache-Control "public, immutable"; } # 关键:销售出库等耗时操作需延长超时 location /admin/instock/salesorder/ { proxy_read_timeout 300; # 默认60秒不够,出库需校验库存+计算成本+生成凭证 proxy_pass http://django_app; } location / { proxy_pass http://django_app; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }4.3 MySQL连接池:用mysqlclient的MAX_CONNS参数防雪崩
在settings.py的DATABASES配置中追加:
'OPTIONS': { 'MAX_CONNS': 20, # 连接池最大连接数 'MIN_CONNS': 5, # 最小空闲连接数 'CONN_MAX_AGE': 0, # 禁用持久连接,避免长事务阻塞 }理由:进销存系统在「月结」期间会并发执行数百个StockMovement查询,若不设连接池上限,MySQL 的max_connections被打满后,新请求将直接失败。CONN_MAX_AGE=0是刻意为之——库存操作必须短平快,长连接反而增加死锁概率。
5. 进阶技巧:用Django Admin Action批量处理库存调整与自定义报表导出
5.1 批量库存调整:绕过单据流的紧急修正方案
当发现扫码错误导致某商品多录入50件时,无需走采购退单流程。在「商品库存查询」列表页:
- 勾选该商品所在行
- 下拉选择「批量库存调整」Action
- 输入调整量
-50,填写调整原因「扫码重复录入」 - 提交
系统执行admin_actions/bulk_stock_adjustment.py中的逻辑:
- 创建
StockMovement记录,movement_type='ADJUST' - 不关联任何业务单据(
related_id=None) - 记录操作人与时间戳
- 发送站内通知给财务组
此操作留痕、可追溯、不影响历史成本,是生产环境必备的救火能力。
5.2 自定义报表导出:用pandas生成带格式的Excel进销存汇总表
系统内置export_inventory_report管理命令,但常需定制字段。以导出「近7天各商品出入库汇总」为例,在management/commands/export_custom_report.py中编写:
import pandas as pd from django.core.management.base import BaseCommand from instock.models import StockMovement class Command(BaseCommand): def handle(self, *args, **options): qs = StockMovement.objects.filter( created_at__gte=timezone.now() - timedelta(days=7) ).values('product__name', 'movement_type').annotate( total_qty=Sum('quantity_change'), total_amount=Sum('cost_amount') ) df = pd.DataFrame(list(qs)) # 添加中文列名与条件格式 df.columns = ['商品名称', '变动类型', '数量合计', '金额合计'] df['变动类型'] = df['变动类型'].map({'IN': '入库', 'OUT': '出库', 'ADJUST': '调整'}) # 导出为Excel,冻结首行 with pd.ExcelWriter('7day_movement.xlsx', engine='openpyxl') as writer: df.to_excel(writer, index=False, sheet_name='7日汇总') worksheet = writer.sheets['7日汇总'] worksheet.freeze_panes = 'A2' self.stdout.write('报表已生成:7day_movement.xlsx')执行python manage.py export_custom_report即可获得带格式的Excel,财务人员可直接用于对账。
5.3 数据库课程设计加分项:在Admin中嵌入库存流水时间轴可视化
利用django-admin-interface的custom_css功能,在admin/base_site.html中注入轻量级时间轴JS:
{% block extrahead %} <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <style> .time-axis-chart { height: 200px; } </style> {% endblock %}然后在admin/productstock.py中重写change_view,添加:
def change_view(self, request, object_id, form_url='', extra_context=None): extra_context = extra_context or {} movements = StockMovement.objects.filter( product_id=object_id ).order_by('-created_at')[:30] extra_context['movements_chart_data'] = json.dumps({ 'labels': [m.created_at.strftime('%m/%d %H:%M') for m in movements], 'data': [m.quantity_change for m in movements], }) return super().change_view(request, object_id, form_url, extra_context)配合前端Chart.js渲染,让库存变动趋势一目了然——这在数据库课程设计答辩中,比纯文字描述「实现了库存管理」更具说服力。
本文还有配套的精品资源,点击获取