简介:这是一套基于Python 3.7与Django 2.2.3开发的资产管理系统完整源码包,适合正在学习Django框架的开发者,以及需要完成毕业设计或课程设计的计算机专业学生。项目涵盖资产管理、分类与位置维护、用户权限控制、Admin后台管理等典型业务模块,清晰演示了Django的MVT设计模式、模型迁移、视图与URL配置、模板渲染和表单处理等核心流程。压缩包共1369个文件,以JS、CSS、HTML等前端静态资源为主,同时包含Python源码、模板配置、SQLite数据库、启动脚本run.bat、AdminLTE后台界面模板以及图片字体等媒体资源,整体约15.05MB,目录结构完整,便于直接运行和二次开发。目前已有158人学习下载。通过该资源可快速理解企业级Web应用的工程组织方式,掌握从环境搭建、数据建模、后台开发到部署上线的全栈实践思路,是一份兼具教学与实用价值的Django项目范例。
1. 拿到压缩包的第一件事:先搞清楚它根本不是“源码堆”
打开这个基于 python 3.7 + django 2.2.3 实现的资产管理系统.zip,你会看到run.bat一马当先,后面跟着一串adminlte.css、iziModal.min.css、fontawesome.css。这不是普通的静态文件堆砌,而是一个已经帮你配好前端皮肤和后端框架的 Django 2.2.3 资产管理系统。它解决的是传统固定资产管理用 Excel 记录导致的“数据不同步、查询靠翻表、权责不清晰”问题,把资产信息、分类、位置、状态统一落到数据库里,通过 Web 界面进行增删改查。对于正在找课程设计、毕业设计模板,或者想在公司内部快速搭一套轻量级 CMDB 的开发者来说,这个包的价值在于“可运行、可改造、前端已集成”。
最不济,它也能当一份 Django 1.x 迁移到 2.2 时代的实战参考。下面我从运行机制讲起,逐步拆到模型、视图、模板和部署,每一层都会给出可复现的命令和代码。
2. 环境选型与一键启动原理:Python 3.7 和 Django 2.2.3 到底卡在哪
2.1 为什么是这个组合,而不是 Django 4.x
Python 3.7 引入的dataclasses、改进的nonlocal和字典访问优化,让它在 2019 年是“生产级”的版本;Django 2.2 是当年的 LTS(长期支持)版本,2.2.3 修复了若干安全漏洞,比如反序列化问题。这个组合放在今天仍然能跑,而且很多企业内部遗留项目就停在这个栈上。如果你后续要接手这类项目,先确认 Python 版本不是 3.8+,因为 Django 2.2 官方支持到 3.7,用 3.9 以上可能会遇到distutils兼容性问题。
从运行角度看,这个 zip 的核心在于run.bat。它通常做的事情就是激活虚拟环境、检查依赖、执行数据库迁移,最后启动开发服务器。常见的写法和我们手动敲命令是等价的。
@echo off cd /d %~dp0 if not exist venv ( python -m venv venv call venv\Scripts\activate pip install -r requirements.txt ) else ( call venv\Scripts\activate ) python manage.py makemigrations python manage.py migrate python manage.py runserver 0.0.0.0:8000逻辑说明:%~dp0获取批处理所在目录,保证无论从哪里双击运行,工作目录都切换正确;虚拟环境不存在时自动创建,并安装requirements.txt中锁定的Django==2.2.3。每次启动先迁移数据库,再启动监听0.0.0.0:8000的服务器,这样做是为了避免你手动敲一堆命令。
参数说明:0.0.0.0表示监听所有网卡,局域网内其他机器可以访问;如果你只本机调试,改成127.0.0.1更安全。
2.2 项目结构与静态文件职责
zip 里出现的 CSS 文件不是乱放的。adminlte.css和adminlte.min.css对应后台管理框架 AdminLTE 的压缩和未压缩版本;iziModal.min.css是弹窗组件样式;all.css、fontawesome.css、v4-shims.css属于 FontAwesome 图标库的各个变体。它们被放在static/目录下,最终通过模板里的{% load static %}引入。
下表列出了这个系统典型的文件结构,你解压后应该对照检查:
| 路径 | 作用 |
|---|---|
manage.py | Django 项目操作入口,所有命令的起点 |
run.bat | Windows 一键启动脚本 |
cmdb/ | 主应用,存放 models、views、admin |
templates/base.html | 公共骨架,集成 AdminLTE 布局 |
static/adminlte/ | AdminLTE 的 CSS/JS 文件 |
static/fontawesome/ | 图标字体文件 |
static/iziModal/ | 弹窗组件 |
requirements.txt | 依赖清单,含django==2.2.3 |
如果你的 zip 里没有requirements.txt,请先执行pip install django==2.2.3,然后看settings.py里INSTALLED_APPS是否注册了cmdb应用。这里最常见的坑是静态文件路径配置:
# settings.py STATIC_URL = '/static/' STATICFILES_DIRS = [ BASE_DIR / "static", ]如果不配置STATICFILES_DIRS,模板里的{% static 'adminlte/adminlte.css' %}会直接 404。这也是为什么很多人双击run.bat后页面能用,但没有样式的原因。
2.3 数据库迁移与初始数据
开发阶段默认使用 SQLite,它不需要额外安装数据库服务,适合课程设计和内部小系统。进入项目根目录后,依次执行以下命令:
python manage.py makemigrations cmdb python manage.py migrate python manage.py createsuperuser逻辑说明:makemigrations会把models.py里的变更生成迁移文件,migrate将迁移文件应用到db.sqlite3。createsuperuser创建管理员账号,之后才能登录 Django Admin 管理资产数据。如果run.bat已经在启动时执行了这些,你只需要补建超级用户。
3. 资产数据模型设计:从 Asset、Category 到 Location 的字段规划
3.1 模型之间的关系
资产管理系统最核心的是资产表Asset,它关联分类Category和位置Location。分类描述“这是什么类的东西”,位置描述“东西放在哪”,资产表本身记录“这个东西的归属和状态”。这种多对一关系是资产系统的标准做法,避免在每条资产记录里重复写字符串,也方便按分类和位置做统计。
下面是一个可直接运行的models.py示例:
from django.db import models class Category(models.Model): name = models.CharField(max_length=50, verbose_name="分类名称") def __str__(self): return self.name class Location(models.Model): building = models.CharField(max_length=100, verbose_name="楼栋") room = models.CharField(max_length=100, verbose_name="房间号") def __str__(self): return f"{self.building}-{self.room}" class Asset(models.Model): name = models.CharField(max_length=100, verbose_name="资产名称") category = models.ForeignKey(Category, on_delete=models.PROTECT, verbose_name="分类") location = models.ForeignKey(Location, on_delete=models.SET_NULL, null=True, verbose_name="位置") sn = models.CharField(max_length=100, unique=True, verbose_name="资产编号") status = models.CharField( max_length=20, choices=[("in_use", "使用中"), ("idle", "闲置"), ("repair", "维修中")], default="idle", verbose_name="状态", ) purchase_date = models.DateField(verbose_name="购买日期") price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="价格") owner = models.CharField(max_length=50, blank=True, verbose_name="领用人") remark = models.TextField(blank=True, verbose_name="备注") def __str__(self): return self.name逻辑说明:Category用PROTECT保护,分类下如果还有资产,删除分类会被阻止,避免产生孤儿数据;Location用SET_NULL,删除位置后资产保留,但位置字段置空,这在资产报废后删除位置时更友好。DecimalField用于货币金额,不能用FloatField,否则在求和时会出现浮点误差。
3.2 字段与迁移的对照
| 字段 | 类型 | 关键点 |
|---|---|---|
name | CharField | 不为空,长度 100 |
sn | CharField | unique 唯一约束,保证资产编号不重复 |
purchase_date | DateField | 日期格式,方便按年份过滤 |
price | DecimalField | 保留两位小数 |
status | CharField + choices | 限定可选状态 |
设计字段时注意,verbose_name是 Django 管理后台和表单自动生成的标签名,建议全部写上中文,不然后台会显示英文变量名。生成并执行迁移,命令与 2.3 节一致,只是要确保在models.py所在的 app 下执行:
python manage.py makemigrations cmdb python manage.py migrate cmdb执行后可以通过sqlite3 db.sqlite3 ".schema cmdb_asset"查看实际建表语句,用于核对索引和约束。
3.3 在 Django Admin 中注册和优化
Django 自带的后台是这个系统最省力的部分。在cmdb/admin.py中注册三个模型,并配置列表展示字段:
from django.contrib import admin from .models import Asset, Category, Location @admin.register(Asset) class AssetAdmin(admin.ModelAdmin): list_display = ("sn", "name", "category", "location", "status", "owner") search_fields = ("sn", "name") list_filter = ("status", "category") list_editable = ("status",)参数说明:list_display控制后台列表页显示的列;search_fields会生成一个搜索框,并对 Sn 和名称做 LIKE 查询;list_filter在右侧生成状态和分类筛选条;list_editable可以不用进入详情页直接修改状态,这对于批量盘点非常好用。注册后,登录http://127.0.0.1:8000/admin就能看到资产表。
4. 视图、URL 与 AdminLTE 模板的整合流程
4.1 路由映射方式
在cmdb/urls.py中,采用传统 FBV 方式映射视图:
from django.urls import path from . import views urlpatterns = [ path("", views.asset_list, name="asset_list"), path("add/", views.asset_add, name="asset_add"), path("edit/<int:pk>/", views.asset_edit, name="asset_edit"), path("delete/<int:pk>/", views.asset_delete, name="asset_delete"), ]<int:pk>是 Django 2.0 以后的 path 转换器,它会把 URL 中的数字自动转换成 Python int 类型,并在视图函数中作为参数接收。相比旧版正则写法,更简洁且类型安全。
对应视图函数views.asset_list需要处理分页和搜索:
from django.core.paginator import Paginator from django.shortcuts import render from .models import Asset def asset_list(request): qs = Asset.objects.select_related("category", "location").all() keyword = request.GET.get("keyword", "") if keyword: qs = qs.filter(name__icontains=keyword) paginator = Paginator(qs, 10) page_obj = paginator.get_page(request.GET.get("page")) return render(request, "cmdb/asset_list.html", {"page_obj": page_obj})逻辑说明:select_related在查询 Asset 的同时用 JOIN 把关联的 Category 和 Location 一次性查出来,避免逐条查询产生 N+1 问题;filter(name__icontains=keyword)实现前后缀模糊搜索。分页器每页 10 条,page_obj在模板中可以调用page_obj.has_previous()和page_obj.has_next()控制翻页按钮。
4.2 AdminLTE 模板引用方式
这个系统采用 AdminLTE 风格,模板继承关系为base.html->asset_list.html。base.html中需要先加载静态文件:
{% load static %} <!DOCTYPE html> <html lang="zh-hans"> <head> <meta charset="UTF-8"> <title>{% block title %}资产管理系统{% endblock %}</title> <link rel="stylesheet" href="{% static 'adminlte/adminlte.css' %}"> <link rel="stylesheet" href="{% static 'fontawesome/all.css' %}"> <link rel="stylesheet" href="{% static 'iziModal/iziModal.min.css' %}"> </head> <body> {% block content %}{% endblock %} <script src="{% static 'iziModal/iziModal.min.js' %}"></script> </body> </html>注意href="{% static '...' %}"不可写成href="/static/...",因为前者会根据部署环境自动处理 CDN 或自定义静态服务器 URL。下表是这些 css 在页面中的角色:
| 文件 | 模块 | 用途 |
|---|---|---|
adminlte.css | 整体布局 | 侧边栏、导航、卡片、表格基础样式 |
all.css | 图标 | 菜单图标、按钮图标 |
iziModal.min.css | 弹窗 | 资产详情弹出层 |
style.css | 自定义覆盖 | 覆盖 AdminLTE 默认颜色和间距 |
asset_list.html中,使用page_obj渲染表格:
{% extends "base.html" %} {% block content %} <div class="card"> <table class="table table-bordered"> <tr> <th>编号</th><th>名称</th><th>分类</th><th>状态</th> </tr> {% for asset in page_obj %} <tr> <td>{{ asset.sn }}</td> <td>{{ asset.name }}</td> <td>{{ asset.category.name }}</td> <td>{{ asset.get_status_display }}</td> </tr> {% empty %} <tr><td colspan="4">暂无数据</td></tr> {% endfor %} </table> </div> <div class="pagination"> {% if page_obj.has_previous %} <a href="?page={{ page_obj.previous_page_number }}">上一页</a> {% endif %} </div> {% endblock %}get_status_display是 Django 对choices字段自动生成的方法,返回中文状态而不是存储值。如果页面没有 css,优先检查STATICFILES_DIRS是否指向了实际解压目录,这个我在 2.2 节已经强调过。
4.3 表单提交与 CSRF 防护
要实现新增资产,模板里必须带上{% csrf_token %}:
<form method="post" action="{% url 'asset_add' %}"> {% csrf_token %} {{ form.as_p }} <button type="submit" class="btn btn-primary">提交</button> </form>Django 2.2.3 默认启用 CSRF 中间件,如果不加这个 token,POST请求会被 403 拒掉。这里form.as_p由 Django 的 ModelForm 自动渲染字段,省去手写每个输入框的 HTML。视图函数里校验逻辑为:
from django.shortcuts import redirect from .forms import AssetForm def asset_add(request): if request.method == "POST": form = AssetForm(request.POST) if form.is_valid(): form.save() return redirect("asset_list") else: form = AssetForm() return render(request, "cmdb/asset_form.html", {"form": form})form.is_valid()会执行字段非空、类型判断和唯一约束检查,只要模型里定义了unique=True,重复的sn会被自动拦截。
5. 部署到宝塔的要点和二次开发的两个小技巧
5.1 用 Gunicorn 和 Nginx 跑起来
如果在 Linux 服务器上部署,不要直接用run.bat。安装 Python 3.7 后,通过宝塔面板的 Python 项目管理器创建虚拟环境,然后安装依赖,执行:
pip install gunicorn python manage.py collectstatic --noinput gunicorn cmdb.wsgi:application -b 0.0.0.0:8000 --workers 3参数说明:--workers 3表示 3 个 worker 进程,对于小系统够用;collectstatic会把所有 app 的静态文件复制到STATIC_ROOT指定的目录,比如/www/wwwroot/your_project/static_assets。然后在 Nginx 配置中把/static/请求转发到这个目录:
location /static/ { alias /www/wwwroot/your_project/static_assets/; }cmdb.wsgi:application是 Django 项目默认生成的 WSGI 入口,宝塔的 Python 管理器通常会让你填这个路径,不要写错成settings.py。
5.2 静态文件 404 的排查顺序
部署后页面无样式,90% 是collectstatic没有执行,或 Nginx 的alias路径末尾少了一个斜杠。还有一种情况是settings.py里没有配置STATIC_ROOT,导致collectstatic直接报错。建议在部署前先在本地执行一次python manage.py collectstatic --dry-run,它会列出将要复制的文件清单,用来验证目录映射是否正确。
5.3 给系统加一个资产导出模块
在当前基础上快速增加 Excel 导出功能,可以在views.py中写一个使用csv模块的导出视图:
import csv from django.http import HttpResponse def export_assets(request): response = HttpResponse(content_type='text/csv') response['Content-Disposition'] = 'attachment; filename="assets.csv"' writer = csv.writer(response) writer.writerow(['编号', '名称', '分类', '状态']) for asset in Asset.objects.all(): writer.writerow([asset.sn, asset.name, asset.category.name, asset.get_status_display()]) return responsecontent_type='text/csv'让浏览器识别为下载文件,Content-Disposition里的filename指定下载名。把这函数加入 URL 后,就可以在列表页加一个导出按钮,点击后直接下载 csv,用 Excel 打开不会乱码(注意 Windows 下可能需要改成 UTF-8 with BOM)。
最后提醒一点,Asset 模型中用DecimalField存价格,导出时asset.price是 Decimal 对象,直接写到 csv 会变成字符串,不影响正确性,但如果你要累加统计,务必在视图里做sum(asset.price for asset in qs),而不是在模板里执行计算。
本文还有配套的精品资源,点击获取