ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Django实战:开发停车场预约计费系统的完整指南

Django实战:开发停车场预约计费系统的完整指南 简介本资源是一套基于Python Django框架开发的停车场预约与计费系统完整源码案例面向Web开发初学者及Django进阶学习者聚焦真实业务场景中的用户管理、车位调度、动态计费与在线支付等核心功能实现。压缩包共2000个文件含1632个JavaScript前端交互脚本支撑预约表单、时间选择器、状态实时更新等、249个HTML模板页面覆盖登录、车位列表、预约确认、订单支付全流程、54个CSS样式文件含bootstrap、font-awesome、datetimepicker等主流UI组件以及5个核心Python后端逻辑文件models/views/urls等整体大小为13.19MB。已有129人下载学习资源结构清晰、模块解耦明确提供从数据库建模、REST式视图设计到第三方支付对接的全链路参考实现特别适合用于课程设计、毕业项目或Django工程化实践训练。2. 停车不再是“碰运气”为什么我决定做一个预约计费系统今年年初我所在的城市核心商圈停车难的问题越来越明显。每天早上八点半写字楼停车场入口就开始排长队进去之后能不能找到车位全凭运气。更让人头疼的是很多车主进场绕一圈发现没位置出场时却因为停留时间不足半小时被收了“入场费”。从停车场运营方的角度看车位利用率其实并不高高峰期拥堵平峰期闲置动态调度全靠管理员拿对讲机喊。这种低效的供需匹配本质上是信息不透明造成的。那段时间我正好在系统地学习Python和Django手头一直在找合适的练手项目。看过博客系统、电商项目、内容管理后台总觉得不太过瘾直到有天在停车场等了二十分钟才等到一个车位我忽然意识到与其做那些“做完就删”的Demo不如干脆自己开发一套“停车场预约停车计费系统”。这样既能解决真实场景里的痛点又能把Django框架的核心能力——ORM建模、用户认证、Session会话管理、Admin后台定制、模板渲染、REST API设计——全部串起来实战一遍。这篇文章就是我把这个项目从零到一完整落地后的复盘包含了业务拆解、数据库设计、计费引擎、并发控制、部署上线以及几处让我印象深刻的坑。适合已经有Python基础、想通过完整项目进阶Django的同学参考。3. 从需求到功能停车场预约计费系统的核心业务拆解开发这类系统最容易犯的错就是一上来就写代码。在没有理清业务边界的情况下代码写得再快后面返工的成本也会成倍上涨。所以第一步我是把整个停车场景里会涉及的“角色”和“动作”一条一条列出来再转成功能模块。3.1 拆解真实场景车主、管理员和运营方各需要什么一个停车场预约计费系统表面上看只是“用户预约、按时计费”实际上至少分成三个角色视角车主C端用户需要能够注册登录、查看停车场实时剩余车位、预约车位、取消预约、入场签到、出场结算、查看历史停车记录。预约时最关心的是“现在还有没有位置”和“大概要花多少钱”。停车场管理员需要审核车位状态、处理异常订单、手动修正计费结果、查看当日营收统计。管理员不一定懂技术所以要给一套足够直观的后台界面。系统运营方超级管理员需要管理多个停车场如果后续扩展、设置计费规则按时长阶梯计价、查看全局的经营报表、管理用户和黑名单。从这三个角色出发我把系统拆成了四个核心模块用户认证与权限管理、停车位信息管理、预约订单管理、计费与结算管理。其中预约和计费是业务的核心链路权限与车位管理是支撑。Django的Contrib模块自带用户认证体系和Admin后台恰好能覆盖权限管理和后台可视化这两块省去了大量重复造轮子的工作。3.2 Django项目与App如何划分模块边界比代码量更重要Django提倡“项目-应用”的结构一个项目可以包含多个App每个App负责一个独立业务域。这是我一开始就确定的划分方式App名称负责的业务域核心模型accounts用户注册、登录、个人信息User扩展Django自带Userparking_lot停车场、车位信息维护ParkingLot, ParkingSpacereservation预约、入场、出场流程Reservation, ParkingRecordbilling计费规则与订单结算BillingRule, BillingOrder之所以把预约和计费拆成两个App是因为预约关心的是“车位状态流转”计费关心的是“金额如何计算”两者未来都可能独立扩展。比如停车场要做优惠券抵扣改动范围会限定在billing内部不会动到reservation的核心逻辑。这个边界划分在后来的开发中帮了大忙——计费规则改了三次预约模块一次都没动过。4. 为什么选Django做底座技术选型和架构设计复盘接触过Python Web开发的朋友都知道主流的框架无非是Django、Flask和FastAPI这三个。我的选择过程没有太多纠结但还是想把自己的思考过程写出来供正在做选型的同学参考。4.1 对比Flask和FastAPIDjango在项目型系统里的原生优势Flask以轻量灵活著称适合做API服务或微服务模块但整个用户认证、ORM、表单处理、Admin后台都需要自己拼接第三方库项目规模一旦变大很容易出现“瓶盖子拧不紧”的尴尬。FastAPI则是异步高性能路线在接口文档自动生成方面非常舒服但ORM和后台管理仍然需要自己去组合SQLAlchemy、Alembic这些组件。而Django最大的优势是“全家桶”自带ORM不用手写SQL模型定义好之后迁移、建表、增删改查一条龙。自带Admin后台只需要在admin.py里注册模型就能得到一个可用的管理界面非常适合停车场管理员这种不写代码的角色。自带用户认证体系登录、注册、Session管理、权限分组开箱即用。成熟的模板引擎服务端渲染页面非常方便无需前后端分离就能完成一套完整可用的业务系统。表单处理与CSRF防护内置的安全机制大大降低了Web应用常见漏洞的风险。对于“预约停车计费”这种典型的业务管理系统快速迭代、可靠运行比极致性能更重要。Django的设计哲学“自带一切”恰好就是最稳妥的选择。4.2 项目目录结构与核心依赖清单我的项目目录结构大致是这样的parking_system/ ├── manage.py ├── requirements.txt ├── config/ # 项目配置 │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── accounts/ # 用户模块 │ ├── models.py │ ├── views.py │ └── forms.py ├── parking_lot/ # 车位模块 │ ├── models.py │ ├── admin.py │ └── views.py ├── reservation/ # 预约模块 │ ├── models.py │ ├── views.py │ └── services.py ├── billing/ # 计费模块 │ ├── models.py │ ├── views.py │ └── calculators.py └── templates/ # 模板文件核心依赖我尽量精简只选了四个Django4.2,5.0 mysqlclient2.2.0 python-dotenv1.0.0 django-crispy-forms2.0Django版本我选了4.2 LTS这个版本是官方长期维护版本安全补丁支持周期长适合正式项目。mysqlclient用来自定义数据库连接python-dotenv用来管理环境变量crispy-forms用来美化表单样式。我是从MySQL上线后才发现python-dotenv的重要性——不把数据库密码放进settings.py而是放进.env文件里这样就算代码传到公开仓库也不用担心密钥泄露。5. 数据模型设计车辆、车位、订单和计费记录的关系梳理这个系统最核心的数据模型有五张表它们之间的关系是整个系统的骨架。我在设计的时候遵循了几个原则尽量用Django自带的User模型做扩展不轻易新建用户表能用状态字段表达的流程节点不单独建表所有时间字段都用DateTimeField方便后续做统计报表。5.1 五张核心表的字段设计与关系拆解User用户表Django自带的User模型已经包含用户名、密码、邮箱等字段我通过OneToOneField扩展了手机号和车牌号from django.contrib.auth.models import User from django.db import models class UserProfile(models.Model): user models.OneToOneField(User, on_deletemodels.CASCADE, related_nameprofile) phone models.CharField(max_length20, verbose_name手机号) plate_number models.CharField(max_length10, verbose_name车牌号) created_at models.DateTimeField(auto_now_addTrue) def __str__(self): return f{self.user.username} ({self.plate_number})车牌号在预约时是关键信息停车场入口的摄像头识别车牌后需要和预约订单里的车牌号做匹配所以这块字段必须冗余在一张独立的Profile表里不能等用户每次预约再手动输入。ParkingLot停车场表考虑到未来系统可能接入多个停车场我把停车场信息独立成一张表class ParkingLot(models.Model): name models.CharField(max_length100, verbose_name停车场名称) address models.CharField(max_length255, verbose_name地址) total_spaces models.IntegerField(default0, verbose_name总车位数) hourly_rate models.DecimalField(max_digits6, decimal_places2, default5.00, verbose_name默认时价) is_active models.BooleanField(defaultTrue, verbose_name是否启用) def __str__(self): return self.nametotal_spaces是总车位数而实时剩余车位数不直接存这表里。剩余车位数是通过“总车位数 - 当前被占用的车位数”动态计算出来的。这样设计的好处是不会出现总车位、剩余车位数据不一致的情况。ParkingSpace车位表每个车位都属于一个停车场通过外键关联class ParkingSpace(models.Model): SPACE_STATUS ( (available, 空闲), (occupied, 已占用), (reserved, 已预约), (maintenance, 维护中), ) lot models.ForeignKey(ParkingLot, on_deletemodels.CASCADE, related_namespaces) space_number models.CharField(max_length20, verbose_name车位编号) status models.CharField(max_length20, choicesSPACE_STATUS, defaultavailable, verbose_name车位状态) class Meta: unique_together (lot, space_number) def __str__(self): return f{self.lot.name} - {self.space_number}车位状态有四种空闲、已占用、已预约、维护中。这里要注意一个关键点预约时选中的车位会从“空闲”变成“已预约”一旦用户入场签到又变成“已占用”离场后恢复为“空闲”。这个状态流转是整个系统的核心逻辑后面在订单状态部分会详细展开。Reservation预约订单表class Reservation(models.Model): STATUS_CHOICES ( (pending, 待入场), (active, 停车中), (completed, 已完成), (cancelled, 已取消), (expired, 已过期), ) user models.ForeignKey(User, on_deletemodels.CASCADE, related_namereservations) space models.ForeignKey(ParkingSpace, on_deletemodels.CASCADE, related_namereservations) start_time models.DateTimeField(verbose_name预约开始时间) end_time models.DateTimeField(verbose_name预约结束时间) status models.CharField(max_length20, choicesSTATUS_CHOICES, defaultpending, verbose_name订单状态) created_at models.DateTimeField(auto_now_addTrue) updated_at models.DateTimeField(auto_nowTrue)这张表是整个业务链路的中枢。用户预约时生成一条pending订单同时把对应车位标记为reserved用户入场后订单由pending变为active车位变为occupied用户离场结算完成后订单变为completed车位再次变为available。BillingOrder计费订单表class BillingOrder(models.Model): reservation models.OneToOneField(Reservation, on_deletemodels.CASCADE, related_namebilling) total_amount models.DecimalField(max_digits8, decimal_places2, default0.00, verbose_name总费用) paid_at models.DateTimeField(nullTrue, blankTrue, verbose_name支付时间) is_paid models.BooleanField(defaultFalse, verbose_name是否已支付) def __str__(self): return f订单{self.id} - {self.total_amount}元计费订单和预约订单是一对一关系。每个预约最终只会产生一条计费记录金额在出场结算时计算并写入。5.2 关键设计决策为什么不直接存“剩余车位数”这个点我特别想展开说一下。最早一版设计里我在ParkingLot表上直接加了一个available_spaces字段每次预约成功就减一离场就加一。听起来很直观但上线测试时立刻遇到了两个问题如果某个订单被取消但取消逻辑没有正确归还车位available_spaces就永久少了一个而且很难排查是哪个订单导致的。多个用户同时预约时available_spaces的并发更新容易产生竞态条件可能出现“显示还有5个车位但实际只卖出4个订单”等情况。后来我改成“总车位数 - 当前非空闲状态的车位数 剩余车位数”这个值完全通过实时查询计算得出不再需要额外维护一个加减计数的字段。虽然每次查询多了一次数据库聚合计算但保证了数据的绝对一致性。对于停车场这种查询频率远低于商品秒杀的场景这点性能消耗完全可以接受。6. 预约订单状态流转从锁定车位到入场签到再到离场结算的完整链路如果说数据模型是系统的骨架那预约订单的状态流转就是整个系统的血脉。这一节我会按用户操作的完整路径把整个链路串起来讲清楚。6.1 预约创建车位锁定与时间校验的实现细节用户选择停车场、选择预约时间段后后端需要做三件事校验所选时间段内该车位是否已被占用。校验用户是否有未完成的预约防止一个用户同时占多个车位。创建预约订单并把车位状态改为“已预约”。时间冲突校验是这里最容易出错的地方。我最初直接用简单的“开始时间小于预约结束时间且结束时间大于预约开始时间”来判断后来发现跨天场景会漏判尤其是用户选择22:00到次日02:00这种跨天时间段。最终我采用的查询方式是参考Django官方文档里的时间重叠判断公式from django.db.models import Q from datetime import timedelta def is_space_available(space, start, end): # 查找与该车位存在时间重叠的未完成订单 overlapping Reservation.objects.filter( spacespace, status__in[pending, active], ).filter( Q(start_time__ltend) Q(end_time__gtstart) ).exists() return not overlapping这个查询条件的意思是已有订单的预约开始时间早于新订单的结束时间且已有订单的预约结束时间晚于新订单的开始时间。只要满足这两个条件就说明时间段有重叠。这个判断方式完美覆盖了跨天、首尾相接各种边界情况推荐直接抄作业。6.2 入场签到与离场结算状态一致性保证用户到达停车场后管理员在后台通过车牌号查询到预约订单确认无误后点击“入场签到”。这时订单状态从pending变为active车位从reserved变为occupied同时记录实际入场时间。这里我踩过一个坑入场时间应该以操作时间为准而不是预约开始时间。用户预约的是10:00但他9:40就到了那么计费应该从9:40开始算。所以实际入场时间要存到Reservation模型的入场字段里而不是直接沿用start_time。虽然这个逻辑很直觉但我在第一版确实写成了“计费从start_time开始”导致早到的用户被少计费了二十分钟。后来加了一个actual_check_in字段才修正过来。离场结算的流程如下管理员输入车牌号找到active状态的订单。系统调用计费引擎根据规则计算应缴金额。生成BillingOrder标记为未支付。用户完成支付后订单变为completed车位状态变回available。整个流程的关键在于入场和离场操作都必须在一个事务里完成保证订单状态和车位状态要么同时成功要么同时失败。我在Django里用transaction.atomic装饰器来包裹这些操作from django.db import transaction transaction.atomic def check_in(reservation_id): reservation Reservation.objects.select_for_update().get(idreservation_id) if reservation.status ! pending: raise ValueError(当前订单状态无法入场) # 更新订单状态 reservation.status active reservation.actual_check_in timezone.now() reservation.save() # 更新车位状态 space reservation.space space.status occupied space.save() return reservationselect_for_update()是这里的关键它会对这行订单数据加锁防止两个管理员同时对同一个订单操作出现重复入场的情况。6.3 状态机一览每个状态从哪里来能到哪里去为了方便理解我把订单状态和车位状态的变化整理成一张表操作订单状态变化车位状态变化用户预约无 → pendingavailable → reserved用户取消预约pending → cancelledreserved → available管理员入场签到pending → activereserved → occupied管理员离场结算active → completedoccupied → available预约时间过期未入场pending → expiredreserved → available这张状态表是整个系统最核心的业务规则。后续如果要做定时任务自动将过期订单置为expired只需扫描end_time早于当前时间且状态仍为pending的订单即可。7. 计费引擎的核心算法阶梯价格、时长计算与订单结算计费是停车系统里最能体现业务复杂度的模块也是用户感知最强的部分。这一节我会把计费引擎的算法设计、边界处理和精度问题展开来讲。7.1 计费规则设计为什么用阶梯计费而不是单一费率早期版本的计费规则非常简单每小时5元不足一小时按一小时算。但这种规则对长时间停车的用户非常不友好停24小时要交120元很多人会选择停在旁边的免费路段导致停车场营收反而下降。后来我参考了真实停车场常见的“阶梯计费”模型规则如下首小时收费10元。之后每小时收费5元。单日封顶收费50元。不足一小时按一小时计算。这个规则的设计逻辑是首小时的高价能筛选出短时停车用户的实际需求时价的梯度能鼓励用户提高停车效率单日封顶则给长时间停车用户一个心理安全线避免“停一天交出一顿饭钱”的惊吓。7.2 核心计费代码时长计算与金额取整的边界处理计费引擎的核心代码如下from datetime import timedelta import math class ParkingFeeCalculator: def __init__(self, first_hour_fee10, hourly_fee5, daily_cap50): self.first_hour_fee first_hour_fee self.hourly_fee hourly_fee self.daily_cap daily_cap def calculate(self, entry_time, exit_time): if exit_time entry_time: raise ValueError(离场时间必须晚于入场时间) total_minutes int((exit_time - entry_time).total_seconds() / 60) total_hours math.ceil(total_minutes / 60) if total_hours 1: return self.first_hour_fee # 计算跨越的天数 days (exit_time.date() - entry_time.date()).days 1 if days 1: # 按天计算每天封顶 total_fee 0 current_start entry_time for _ in range(days): day_end current_start.replace(hour23, minute59, second59) if day_end exit_time: day_end exit_time day_minutes int((day_end - current_start).total_seconds() / 60) day_hours math.ceil(day_minutes / 60) if day_hours 1: day_fee self.first_hour_fee else: day_fee self.first_hour_fee (day_hours - 1) * self.hourly_fee total_fee min(day_fee, self.daily_cap) current_start day_end timedelta(seconds1) return total_fee # 单日内计算 fee self.first_hour_fee (total_hours - 1) * self.hourly_fee return min(fee, self.daily_cap)我重点解释几个设计细节。一是math.ceil向上取整这个决定了“不足一小时按一小时算”的规则。二是跨天场景处理停22:00到次日08:00这种单日封顶逻辑必须按天拆分计算否则会错误地把10个小时全部算进一天的封顶里系统就会漏收钱。三是daily_cap的兜底逻辑不管怎么算单日费用不会超过设定上限。7.3 金额精度与四舍五入问题DecimalField搭配decimal_places2直接处理金额Python的Decimal类型不会出现浮点数0.10.20.30000000000000004这种精度问题。但注意计算小时数用的是int类型计算金额时用Decimal转换避免float混入运算。8. 并发场景下的关键实现防止车位超卖和重复结算停车场系统虽然不是电商秒杀级别的高并发但“超卖”和“重复结算”这类问题在预约场景中依然存在而且一旦发生用户的投诉成本非常高。这一节我单独拿出来讲。8.1 超卖问题的本质与解决方案超卖问题的本质是多个请求同时读到一致的数据然后基于这个数据做判断最后写入时产生冲突。在预约流程中两个用户同时看到车位A是空闲的同时发起预约如果后端处理不当两个人都会成功但车位只有一个就超卖了。Django的解决方案是使用select_for_update()锁行。在事务中先锁定该车位对应数据行确保同一时间只有一个订单能处理这个车位from django.db import transaction transaction.atomic def create_reservation(user_id, space_id, start_time, end_time): with transaction.atomic(): space ParkingSpace.objects.select_for_update().get(idspace_id) if space.status ! available: raise ValueError(车位已被占用) # 检查时间段冲突 if not is_space_available(space, start_time, end_time): raise ValueError(该时间段已被预约) # 创建预约 reservation Reservation.objects.create( user_iduser_id, spacespace, start_timestart_time, end_timeend_time, statuspending, ) space.status reserved space.save() return reservationselect_for_update()要求整个操作必须在事务中执行Django的transaction.atomic提供了这个上下文。这个方案能有效防止重复预约但要注意锁的粒度锁的是车位行而不是整个停车场。所以不同车位之间互不影响不会造成全局锁导致的性能下降。8.2 重复结算的兜底方案离场结算时同样需要用select_for_update()锁住订单并校验订单状态必须是active。如果管理员不小心点击两次“结算”第二次操作会因为订单状态已经不是active而被拒绝。另外还需要设置一个幂等标识。我在BillingOrder表上加了一个unique约束的reservation外键这样即使两个请求同时尝试创建计费订单数据库层面也能保证同一个预约只能产生一条计费记录。如果使用了PostgreSQL或MySQL的unique约束Django ORM的IntegrityError就能兜住并发冲突。8.3 测试并发场景的工具选择本地开发时我用了Apache Benchab和Python的并发请求库来测试。比如用ab模拟50个并发请求同时预约同一个车位验证最终生成的订单数只有1个。具体命令ab -n 50 -c 50 -p payload.json -T application/json http://127.0.0.1:8000/api/reserve/如果系统没有正确加锁这50个请求里会出现多个success响应加锁正确的话49个会拿到“车位已被占用”的提示。9. 部署实战从SQLite迁移到MySQL以及生产环境的配置细节开发阶段我直接用Django默认的SQLite数据库零配置、文件存储、查起来也方便。但正式部署就必须切换到MySQL或PostgreSQL否则生产环境并发一高SQLite的写锁问题会被无限放大。9.1 迁移数据库时最容易踩的坑从SQLite迁移到MySQL我遇到的最大的坑是字符集和排序规则。SQLite对Emoji和生僻字的支持不如MySQL严格迁移过去后如果MySQL表用的是utf8mb4_general_ci插入Emoji字符时会出现“Incorrect string value”的报错。解决方案是创建数据库时显式指定字符集CREATE DATABASE parking_system CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;另一个坑是时间字段的存储。SQLite存储的是字符串MySQL存储的是DATETIME类型迁移时如果之前用了很多字符串格式化操作现在需要统一改成Django的timezone工具类来生成和管理时间否则会导致时区错乱。迁移步骤建议用Django自带的命令分两步走python manage.py makemigrations python manage.py migrate第一次执行migrate之前一定要先备份SQLite文件。因为有些数据迁移操作是不可逆的一旦执行错误没有备份连后悔药都没得吃。9.2 Nginx uWSGI部署的简化配置部署我采用的是Nginx做反向代理uWSGI作为应用服务器。uWSGI的配置文件大概长这样[uwsgi] chdir /var/www/parking_system module config.wsgi:application master true processes 4 threads 2 socket 127.0.0.1:8001 vacuum true die-on-term true env DJANGO_SETTINGS_MODULEconfig.settings.productionNginx配置里把/api/和/static/的请求代理到uWSGI服务其余静态文件直接交给Nginx处理server { listen 80; server_name example.com; location /static/ { alias /var/www/parking_system/static/; } location / { include uwsgi_params; uwsgi_pass 127.0.0.1:8001; } }有一点值得提醒Django的DEBUG模式在生产环境必须关闭否则一旦出现异常完整的报错信息、文件路径、数据库配置都会直接暴露给用户这是很严重的安全隐患。10. 踩坑记录开发调试过程中最容易翻车的几个隐蔽问题最后这部分我想把自己在开发这个项目过程中印象最深的几个坑整理出来这些细节很难在官方文档里查到但实际开发中几乎必然会碰到。10.1 Django时区设置导致的时间偏移开发过程中我发现预约时间总是比实际时间慢8小时。排查了很久发现问题出在settings.py里的TIME_ZONE配置。Django默认的TIME_ZONE是UTC如果项目不设置USE_TZTrue且TIME_ZONEAsia/Shanghai所有的时间字段都会按UTC存储模板显示时也不会自动转换。最终配置如下USE_TZ True TIME_ZONE Asia/Shanghai设置了TIME_ZONE之后模板中需要显示本地时间时用模板过滤器{{ order.start_time|localtime|date:Y-m-d H:i }}10.2 Admin后台注册模型后复选框宽度异常这是个比较冷门但很影响体验的问题。Admin后台的ManyToManyField默认用复选框展示如果关联对象特别多复选框会挤成一列几乎没法操作。我之前设计的预约记录和车位关联场景就遇到了这个问题。解决方案是自定义ModelAdmin用filter_horizontal属性将多选框改成左右两栏的选择器class ReservationAdmin(admin.ModelAdmin): filter_horizontal (spaces,)这个配置让后台管理体验提升了不止一个档次。10.3 静态文件404导致页面样式全丢开发模式下Django能正常处理静态文件但部署后静态文件404是很常见的现象。原因是在生产环境里Django默认不会托管静态文件需要先执行collectstatic把所有静态文件收集到一个目录再由Nginx处理python manage.py collectstatic --noinput我在首次部署时忘记配置STATIC_ROOT结果页面HTML正常加载但所有CSS和JS全部404看起来就像网站“裸奔”了。10.4 订单超时未支付的处理预约成功但用户始终没有支付、也没有入场车位上会一直挂着reserved状态。我设计了一个Django管理命令定时扫描所有end_time早于当前时间且状态为pending的订单将它们自动标记为expired并释放车位from django.core.management.base import BaseCommand class Command(BaseCommand): help 处理过期预约 def handle(self, *args, **options): expired_reservations Reservation.objects.filter( statuspending, end_time__lttimezone.now() ) for reservation in expired_reservations: with transaction.atomic(): reservation.status expired reservation.save() reservation.space.status available reservation.space.save()然后通过crontab配置每小时执行一次0 * * * * cd /var/www/parking_system /usr/bin/python3 manage.py expire_reservations /var/log/parking_expire.log 21这个定时任务是整个系统能够长期稳定运行的重要保障。没有它只要有几个用户预约后不来运营方就不得不在后台频繁手动释放车位。11. 项目未来的扩展方向到这里一个完整的“Python基于Django停车场预约停车计费系统”的核心内容已经全部落地了。最后聊聊这个项目的扩展空间。我目前正在做的是给系统增加LPR车牌识别摄像头的对接通过OpenCV识别入场车辆的车牌号码后自动匹配预约订单并放行真正实现“预约即通行”。远期规划里还会考虑对接微信小程序预约入口、停车费预充值账户、以及基于时间区间价格的动态调价。如果你也在用Django做类似的项目管理系统这套模块划分、状态机设计和并发控制方案可以直接平滑移植希望这篇复盘能帮你少走一些弯路。本文还有配套的精品资源点击获取
返回列表