Vue+Python+MySQL车辆租赁管理系统设计与实现
发布时间:2026/9/16 19:42:55 作者:尧图编辑部 阅读量:1,286

最近在做车辆租赁管理系统这类项目我遇到最多的技术组合就是 Vue PythonFlask 或 Django MySQL。这个选题确实挺典型的前后端分离、业务状态多、还要有后台管理几乎覆盖了 Web 开发的完整链路。这篇文章我把这套车辆租赁信息管理系统的设计和落地方案完整梳理一遍包括技术选型思路、数据库建模、接口设计、Vue 页面搭建以及遇到的坑和排查方法给正在做同类毕设或练手项目的朋友一个可以直接参考的路线。1. 项目整体设计与技术选型思路1.1 核心业务与系统功能划分做项目前先把业务拆清楚。车辆租赁系统的核心就是“人有车可租、后台管得住”这件事。从用户视角看要能注册登录、浏览车辆、提交租车订单、查看自己的订单状态从管理员视角看要能维护车辆信息、审核订单、处理还车、管理用户账号。整个过程的核心是数据尤其是车辆状态和订单状态的流转。我把系统拆成两类角色、三大模块来设计。两类角色是普通用户和后台管理员用户端的诉求是快速找车、快速下单管理员端的诉求是车辆库存实时可见、订单状态可追踪。三大模块分别是用户管理模块、车辆管理模块、租赁订单模块。用户管理模块负责注册、登录、身份信息维护管理员可以查看用户列表、禁用异常账号。这个模块是权限体系的基础几乎后面所有业务接口都要过认证这一关所以身份验证和权限控制要优先设计好。车辆管理模块维护车的基本信息包括车牌、品牌、车型、日租金、当前状态。状态字段是整个系统的关键它会被订单模块联动修改车被下单后自动变成已租归还后恢复可租。如果状态流转设计得不严谨很容易出现同一辆车被重复下单的数据脏读问题。租赁订单模块是业务的终点用户提交租车订单包含取车时间、预计还车时间管理员在后台核验信息后确认订单还车时系统计算实际费用。金额计算逻辑是这里的重点因为涉及按天计费、超时计费、押金退还等场景。1.2 为什么选 Vue Python 这套组合技术选型要回到项目特征来想。这种管理系统业务逻辑不算复杂但列表多、表单多、状态多前后端需要频繁配合。Vue 适合做前端是因为组件化开发模式对这种“页面多但结构有规律”的场景非常友好。车辆列表抽成一个组件订单卡片抽成一个组件后台表格再抽一个组件放哪里都能复用开发效率比传统 jQuery 拼页面高出一大截。Python 负责后端核心优势是开发效率高。Django 自带的 ORM写一个模型类就能自动映射数据库表DRFDjango REST Framework可以快速把 Model 转成 RESTful 接口少写大量样板代码。如果说 Java 是工地上的重型机械那 Python Django 更像一把顺手的电动螺丝刀拧得快而且足够稳。这套组合的另一大优势是生态成熟。前端有 Element Plus 这类现成组件库后端有 Django Admin、simpleui 这类后台方案数据库迁移、用户认证、JWT 等都有成熟库可用。对于一个人从零开发一个完整系统来说技术债很低交付速度也快。1.3 Flask 还是 Django别再纠结了标题里同时出现了 Flask 和 Django确实是不少初学者最纠结的选择。我判断的标准很简单看项目有没有“后台管理”需求。Django 自带成熟的 Admin 后台模型注册一下增删改查页面就出来了配合 simpleui 这类美化插件视觉效果也过得去。Django 还有完整的用户认证体系、ORM、表单处理项目需要的基础设施基本都内置了不需要自己组装。Flask 的优势是轻。核心框架非常精简路由和请求处理很直接适合那种后端纯 API、前端完全独立部署的小项目。但真要做一个带管理后台的完整系统Flask 需要自己组装 Flask-SQLAlchemy、Flask-Login、Flask-Admin 这些扩展。每个扩展都有独立的配置方式和版本兼容要求组装成本其实不低。结合这个项目的特点我的建议很明确这类“系统管理型”项目优先选 Django。如果只是给小程序或 App 提供轻量 API不需要后台界面再考虑 Flask。下文整套代码以 Django 为主Flask 方案的组件对照我会在结尾单独列出思路完全通用。2. 开发环境与工程初始化2.1 Pycharm 安装与 Python 虚拟环境开发环境这块是第一个容易翻车的地方。Pycharm 我推荐直接用社区版对这类本地开发项目来说功能完全够用专业版的远程解释器、数据库工具对个人项目不是刚需。安装 Python 时有个细节特别提醒务必勾选“Add Python to PATH”否则后面在终端执行 python 命令会直接提示找不到。版本选择上Python 3.9 或 3.10 最稳Django 4.x LTS 版本对 3.9 支持很好。项目创建后第一时间建虚拟环境在 Pycharm 底部面板的 Terminal 里执行python -m venv venvWindows 下激活命令venv\Scripts\activate激活后终端提示符前面会出现 (venv)说明已经进入虚拟环境。所有依赖装在这个隔离空间里不会污染系统 Python后面换机器部署时只需要 requirements.txt 一条命令就能恢复环境。我见过太多人图省事直接全局 pip install最后版本冲突到想砸电脑。2.2 MySQL 数据库准备与驱动坑车辆租赁系统的数据量用 MySQL 8.0 完全够建库时字符集必须指定 utf8mb4不然后面存中文会有编码隐患CREATE DATABASE vehicle_rental DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;Django 连接 MySQL 默认需要 mysqlclient但 Windows 上直接 pip install mysqlclient 经常会报错因为缺少本地编译环境。不想折腾 Visual Studio 编译工具的话用 pymysql 替代最省事在项目配置文件的init.py 里加两行import pymysql pymysql.install_as_MySQLdb()这个方法实测最稳本地开发推荐直接用。上线环境如果追求更优的性能再换回 mysqlclient 也不迟。2.3 Vue 项目脚手架搭建与依赖安装前端部分我选择 Vue 3 Vite 的组合用官方脚手架初始化npm create vuelatest初始化过程会问是否需要 Router、Pinia 等按需选择即可。基础项目生成后安装核心依赖npm install npm install axios vue-router element-plus dayjsVite 的冷启动速度比 webpack 时代快得多开发体验很好。热词里提到的“vue 安装依赖”卡顿问题多半是网络原因导致的建议先切换国内镜像源npm config set registry https://registry.npmmirror.com镜像源切换后安装速度会快不少再也不用盯着进度条怀疑人生。3. 后端车辆、用户、订单三大核心模块的实现3.1 数据库模型设计后端按 Django 标准流程走创建项目、创建应用、写模型、迁移数据库、写接口。落代码前先把表结构理清楚我用一张表总结核心设计。表名核心字段说明User继承 AbstractUserphone、id_card、license_no扩展 Django 自带用户模型Vehicleplate_number、brand、model_name、rent_price、status、image车辆基本信息与状态RentalOrderorder_no、user外键、vehicle外键、pickup_time、return_time、actual_return_time、estimated_price、actual_price、status订单全生命周期数据Django 自带的 User 模型已经覆盖用户名、密码、邮箱、权限分组这些基础能力不要自己写密码哈希逻辑那是纯给自己挖坑。业务字段通过继承 AbstractUser 扩展即可。车辆表的状态字段用 IntegerField 配 choices 更规范0可租、1已租、2维修、3下线比直接存字符串更容易做筛选和状态机控制。订单表是核心中的核心用户外键和车辆外键都要加 db_index因为后面在用户中心查订单、在车辆详情页看历史租车记录都会走这两个索引。金额字段用 DecimalFieldmax_digits10, decimal_places2不要用 FloatField。涉及钱的计算浮点误差是不能接受的这是电商和租赁系统开发的铁律。from django.db import models from django.contrib.auth.models import AbstractUser class User(AbstractUser): phone models.CharField(max_length20, blankTrue) id_card models.CharField(max_length18, blankTrue) class Vehicle(models.Model): STATUS_CHOICES ( (0, 可租), (1, 已租), (2, 维修), (3, 下线), ) plate_number models.CharField(max_length20, uniqueTrue) brand models.CharField(max_length50) model_name models.CharField(max_length50) rent_price models.DecimalField(max_digits10, decimal_places2) status models.IntegerField(choicesSTATUS_CHOICES, default0) image models.ImageField(upload_tovehicles/, blankTrue) class RentalOrder(models.Model): STATUS_CHOICES ( (0, 待提车), (1, 租赁中), (2, 已归还), (3, 已取消), ) order_no models.CharField(max_length32, uniqueTrue) user models.ForeignKey(User, on_deletemodels.CASCADE, db_indexTrue) vehicle models.ForeignKey(Vehicle, on_deletemodels.PROTECT, db_indexTrue) pickup_time models.DateTimeField() return_time models.DateTimeField() actual_return_time models.DateTimeField(nullTrue, blankTrue) estimated_price models.DecimalField(max_digits10, decimal_places2) actual_price models.DecimalField(max_digits10, decimal_places2, nullTrue, blankTrue) status models.IntegerField(choicesSTATUS_CHOICES, default0)模型定义好后执行迁移命令生成数据表。热词里也有“django执行查询-删除对象”这类基础问题像管理员批量清理历史取消订单用 ORM 的 filter().delete() 最直接RentalOrder.objects.filter(status3, pickup_time__lt2024-01-01).delete()3.2 用 DRF 快速生成 RESTful APIDjango 开发主流的 API 方案是 DRF核心就三样serializers.py 做序列化、views.py 写视图、urls.py 注册路由。以车辆列表接口为例# serializers.py from rest_framework import serializers from .models import Vehicle class VehicleSerializer(serializers.ModelSerializer): class Meta: model Vehicle fields [id, plate_number, brand, model_name, rent_price, status, image]视图用 ViewSet它已经封装好列表、详情、增删改查全部逻辑# views.py from rest_framework import viewsets from .models import Vehicle from .serializers import VehicleSerializer class VehicleViewSet(viewsets.ModelViewSet): queryset Vehicle.objects.all() serializer_class VehicleSerializer路由注册# urls.py from rest_framework.routers import DefaultRouter from .views import VehicleViewSet router DefaultRouter() router.register(vehicles, VehicleViewSet) urlpatterns router.urls这套流程下来车辆列表、车辆详情、新增、修改、删除的 HTTP 接口全部就绪。访问 /api/vehicles/ 就能拿到 JSON 数据前端只需要对接接口就行。订单模块的接口需要更多业务逻辑比如提交订单时要把车辆状态改成已租。这里推荐用 Django 的 transaction.atomic 包裹保证“创建订单 更新车辆状态”两个操作要么都成功、要么都回滚from django.db import transaction from rest_framework.decorators import action class RentalOrderViewSet(viewsets.ModelViewSet): queryset RentalOrder.objects.all() serializer_class RentalOrderSerializer transaction.atomic def create(self, request, *args, **kwargs): vehicle_id request.data.get(vehicle) vehicle Vehicle.objects.select_for_update().get(idvehicle_id) if vehicle.status ! 0: return Response({detail: 车辆不可租}, status400) vehicle.status 1 vehicle.save() return super().create(request, *args, **kwargs)select_for_update 会对这行记录加锁并发场景下不会出现两个人同时租同一辆车的问题这个细节很关键。3.3 Django Admin 后台配置与界面美化Django 自带 Admin 是信息管理系统的一颗糖。在 admin.py 里注册模型后台增删改查页面就出来了from django.contrib import admin from .models import Vehicle, RentalOrder admin.site.register(Vehicle) admin.site.register(RentalOrder)但原生样式确实过于朴素。热词里提到的“django admin 界面美化”我推荐 simpleuipip 安装后注册到 INSTALLED_APPS 即可界面会变成现代化风格菜单、搜索、筛选都清晰很多。INSTALLED_APPS [ simpleui, django.contrib.admin, # ... ]Admin 的自定义 actions 也很有用我做过一个批量还车的操作选中多个订单一键完成还车并计算金额def mark_returned(modeladmin, request, queryset): for order in queryset: order.complete_return() messages.success(request, f已处理 {queryset.count()} 个订单) mark_returned.short_description 完成还车并计算费用这类批量操作非常适合管理员处理日常高频重复工作。3.4 用户注册登录与 JWT 认证前后端分离项目推荐用 JWT 做认证后端不需要维护 session前端拿到 token 存 localStorage每次请求时带在请求头里。安装配置 djangorestframework-simplejwt 后在 settings.py 里启用REST_FRAMEWORK { DEFAULT_AUTHENTICATION_CLASSES: ( rest_framework_simplejwt.authentication.JWTAuthentication, ), }URL 里注册登录和刷新 token 的路由from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView urlpatterns [ path(api/auth/login/, TokenObtainPairView.as_view()), path(api/auth/refresh/, TokenRefreshView.as_view()), ]注册接口需要自定义视图因为要额外保存手机号和身份证号。注意密码字段一定要用 Django 的 create_user 方法加密保存不能直接存明文。4. 前端Vue 页面与接口对接4.1 Vue 项目结构与路由配置后端接口就绪后前端工作分两块页面构建和接口对接。Vite 初始化的项目结构很清晰src/ api/ # 接口请求函数 assets/ # 静态资源 components/ # 通用组件 router/ # 路由配置 views/ # 页面组件 App.vue main.js路由结构按角色划分车辆列表和详情是公开页面用户中心、订单页面需要登录态后台管理页面需要管理员权限。vue-router 4 的配置方式const routes [ { path: /, component: HomeView }, { path: /vehicles, component: VehicleList, meta: { public: true } }, { path: /orders, component: OrderList, meta: { requiresAuth: true } }, { path: /admin, component: AdminLayout, meta: { requiresAdmin: true } }, ]在全局前置守卫里统一判断router.beforeEach((to, from, next) { const token localStorage.getItem(token) const role localStorage.getItem(role) if (to.meta.requiresAuth !token) next(/login) else if (to.meta.requiresAdmin role ! admin) next(/) else next() })路由守卫是前端权限控制的第一道关卡配合后端接口权限校验形成双保险。4.2 车辆展示与租车下单流程车辆列表页用 Element Plus 的 el-card 组件展示车辆信息每张卡片显示品牌、车型、日租金车况状态以标签形式展示。车辆卡片抽成独立组件template el-card classvehicle-card img :srcvehicle.image / h3{{ vehicle.brand }} {{ vehicle.model_name }}/h3 p日租金¥{{ formatPrice(vehicle.rent_price) }}/p el-tag :typevehicle.status 0 ? success : danger {{ vehicle.status 0 ? 可租 : 已租 }} /el-tag el-button typeprimary :disabledvehicle.status ! 0 clickhandleRent 立即租车 /el-button /el-card /template点击“立即租车”进入下单流程选择取车时间和还车时间。前端用 dayjs 计算租车天数和预估金额const days dayjs(endDate).diff(dayjs(startDate), day) estimatedPrice.value days * vehicle.rentPrice这里要特别提醒前端计算的金额仅供参考提交订单时后端必须重新计算。我在开发中踩过这个坑用户直接改请求参数把价格改成 0 下单成功后来后端加了重新计算校验才堵住这个漏洞。订单提交成功后跳转到订单列表页。订单列表按状态分 Tab全部、待提车、租赁中、已归还、已取消每个状态对应不同的操作按钮例如租赁中的订单可以发起“申请还车”待提车的订单可以取消。4.3 Axios 封装、环境变量与跨域处理请求接口前我习惯先封装一个统一请求工具集中处理 token、超时、错误提示import axios from axios const request axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 10000, }) request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) config.headers.Authorization Bearer ${token} return config }) request.interceptors.response.use( res res.data, err { if (err.response err.response.status 401) { localStorage.removeItem(token) router.push(/login) } return Promise.reject(err) } )baseURL 从环境变量读取开发环境和生产环境指向不同的后端地址.env.development 内容为VITE_API_BASE_URLhttp://localhost:8000.env.production 内容为VITE_API_BASE_URL/api开发阶段的跨域问题有两种解决方式一种是在后端安装 django-cors-headers把前端地址加入白名单另一种是在 Vite 配置开发服务器代理。我在实际开发中两种都用前端配代理减少改动后端配 CORS 兜底。5. 常见问题与排查技巧实录5.1 Django 连接 MySQL 的三连坑热词里“django install mysqlclient”是高频搜索词说明跨平台连接 MySQL 的问题确实折磨了不少人。我总结三个最经典的坑和对应解法第一个坑是 mysqlclient 装不上。Windows 下需要本地有编译环境不想折腾就换 pymysql在 django 项目同名目录的__init__.py里声明替代import pymysql pymysql.install_as_MySQLdb()第二个坑是数据库字符集不对。建库时必须指定 utf8mb4不然后面写入中文时有时会出现异常或者乱码。第三个坑是忘记迁移或迁移顺序混乱。Django 的模型和数据库表是两套体系每次更改模型都要执行python manage.py makemigrations python manage.py migrate有一个更隐蔽的问题如果有多个 Django Appmakemigrations 会生成多个迁移文件migrate 时偶尔会提示依赖错误。解决思路是删除所有 App 的 migration 文件保留__init__.py并重建数据库开发阶段这个操作没有风险。5.2 Vue 打包后布局异常与白屏排查热词里“vue 打包后 布局异常”出现的频率也非常高。本地 npm run dev 一切正常打包部署后要么白屏、要么 CSS 全部丢失几乎都是 publicPath 配置导致的。Vite 默认的 base 是/部署在域名根路径没问题但如果部署在类似http://example.com/rental/的子路径下静态资源会全部 404。解决方法是修改 vite.config.jsexport default defineConfig({ base: ./, })改成相对路径后打包出来的 HTML 里所有资源引用都是相对当前路径的部署到任何子目录都能正常加载。路由模式也推荐用 hash 模式避免 history 模式部署到 Nginx 后刷新页面 404 的问题createWebHashHistory()5.3 前后端联调的时间字段与金额精度问题联调过程中最容易出乱子的是时间字段。Django 的 DateTimeField 用 DRF 序列化后默认输出的是 ISO 格式类似2024-06-11T10:30:00Z前端直接展示非常不友好。在 settings.py 里全局配置时间格式REST_FRAMEWORK { DATETIME_FORMAT: %Y-%m-%d %H:%M:%S, }配置之后所有接口的时间字段都会统一输出成可读格式前端就不需要再转换了。金额显示也有细节问题。后端用 DecimalField 计算精确前端拿到后如果直接展示会出现123.0000这种奇怪格式所以前端要统一做格式化function formatPrice(price) { return Number(price).toFixed(2) }在列表页、订单详情页、用户中心所有涉及金额展示的地方都用这个函数格式化UI 就整洁很多。5.4 生产环境部署的静态文件问题Django 的 DEBUG 设为 False 后静态文件不会自动服务后台管理页面会变成没有样式的内容页面。固定操作是执行 collectstatic 收集静态文件到指定目录python manage.py collectstatic然后在 Nginx 配置中把/static/路径指到收集目录其他请求反向代理到 Django 服务。Vue 构建出的 dist 目录直接作为 Nginx 的 root前后端就完成了分离部署。这里有一个容易漏的配置Django 的 ALLOWED_HOSTS 在生产环境要填上实际域名或 IP否则访问时会报 400 错误。最后补充一下 Flask 方案的组件对照给坚持用 Flask 路线的朋友参考Django Admin 对应 Flask-AdminORM 对应 Flask-SQLAlchemy用户认证对应 Flask-JWT-ExtendedAPI 层可以用 Flask-RESTX 或直接手写 Blueprint。核心思路和数据结构设计都是相通的把车辆、用户、订单这三个业务实体的状态流管好系统的主干就立住了。这次开发车辆租赁系统最大的感受是这类“管理系统型”项目看着简单但要真正让它稳定跑起来从数据库约束到接口权限从前端组件复用到部署配置每一层都有细致活。做完这个项目我对 Web 开发全链路的理解确实扎实了不少希望这篇复盘也能帮你少走点弯路。