之前发布的文章实现一个简单的停车场管理系统,有很多同学私信了多修改项。

【Python+Django】一个简单Web停车场管理系统的快速实现(极简版)文章浏览阅读688次,点赞4次,收藏13次。本文利用Python的Django框架从0开始最终实现一个简单的Web 停车场管理系统。目前一个典型的停车场管理包含如下几个功能:1、扫描车牌识别自动出入场2、会员管理(充值,提醒充值。。等)3、收费(如果是有效期内的会员就免费)为了实现上面几个功能,我们先简单的做下数据库和页面的设计。● 停车场管理系统 ○ 会员管理 ■ 会员充值 ■ 会员查看 ○ 车辆入场管理 ■ 车牌识别 ■ 入场记录 ○ 车辆出场管理 ■ 车牌识别 ■ 出场记_parkmanage 数据库iphttps://blog.csdn.net/agelee/article/details/130229606

花了一些时间在原来的基础上做了些升级改造,完善了注册登录及页面的样式的优化。

目录

软件及版本

停车场管理系统的功能模块设计

数据库设计

页面及功能设计

系统实现

系统操作运行预览

注册登录

首页(Dashboard)

报表

会员充值

会员管理

车辆入场

车辆出场

出场付费

完整文件结构目录

系统实现代码

定义注册登录的Form

定义模型

视图实现

调用百度云图像识别视图

车辆入场视图

车辆出场功能视图

首页(Dashboard)实现

注册登录登出视图

模板实现

首页模板

车辆入场模板

路由URL实现


软件及版本

开发相关的技术和软件版本如下:

服务端:Python 3.9

Web框架:Django 4.10

数据库:MySQL mysql-8.0.13-winx64

开发工具IDE:Pycharm

大家参考学习的时候最好使用相同的版本。

停车场管理系统的功能模块设计

一个典型的停车场管理系统,主要包含如下几个功能模块:

1、会员管理:会员注册,会员充值,会员查看和修改。

2、车辆入场管理:车牌扫描,入场记录

3、车辆出场管理:车牌扫描,出场记录,收费管理

4、报表数据管理:dashboard,出入场记录

5、用户管理:用户注册及登录

数据库设计

按照前面的功能模块,我们需要自定义数据表如下:

会员表:vip_user

车辆记录表:car_record

后续为了实现车牌扫描功能,我们还需要增加扫描结果记录表:license_plate

另外我们为了快速实现系统,用户管理功能实现我们直接基于Django自带的用户及认证模块。

以下是各表结构

vip_user 表:

字段名

类型

最大长度

特性

备注

id

IntegerField (自增)

主键

name

CharField

32

姓名

carnum

CharField

32

唯一

车牌号

phone

CharField

32

手机号

begintime

DateTimeField

入场时间

endtime

DateTimeField

离场时间

user_id

OneToOneField(User)

CASCADE(级联删除)

外键,关联用户

car_record 表:

字段名

类型

最大长度

特性

备注

id

IntegerField (自增)

主键

carnum

CharField

32

车牌号

intime

DateTimeField

入场时间

outtime

DateTimeField

可为 null,允许为空

出场时间

paytime

DateTimeField

可为 null,允许为空

收费时间

amount

IntegerField

可为 null,允许为空

收费金额

License_plate 表:

字段名

类型

最大长度

特性

备注

id

IntegerField (自增)

主键

car_img

ImageField

上传到 ‘car_imgs’ 目录

车辆图片

car_num

CharField

32

唯一

车牌号

color

CharField

32

车牌颜色

Charge_type 表:

字段名

类型

最大长度

特性

备注

id

IntegerField (自增)

主键

type

CharField

32

套餐

amount

IntegerField

可为 null,允许为空

费用(元)

remark

CharField

50

备注

页面及功能设计

为了实现我们前面的功能模块我们设计如下几个功能页面:

1、登录页面:

其中需要登录,校验,登录后同时需要存储用户信息在Session中,以备登录后的页面使用。

2、注册页面:

提供注册信息表单,提交注册通过后,系统生成一个新的用户。

3、首页(Dashboard):

通过卡片和图表的形式展示停车场的关键信息

4、车辆入场管理:

车牌扫描,入场记录

5、车辆出场管理:

车牌扫描,出场记录,收费管理

6、报表数据管理:

dashboard,出入场记录

7、用户管理:

用户注册和登录功能实现

系统实现

系统操作运行预览

注册登录

首页(Dashboard)

报表

会员充值

会员管理

车辆入场

车辆出场

出场付费

实现过程参考

【Python+Django】一个简单Web停车场管理系统的快速实现(极简版)_parkmanage 数据库ip-CSDN博客

完整文件结构目录

系统实现代码

代码较多,以下为一些关键代码

定义注册登录的Form

app01/forms.py

# 引入表单类from django import forms# 引入 User 模型from django.contrib.auth.models import User# 登录表单,继承了 forms.Form 类class UserLoginForm(forms.Form):username = forms.CharField()password = forms.CharField()# 注册用户表单class UserRegisterForm(forms.ModelForm):# 复写 User 的密码password = forms.CharField()password2 = forms.CharField()class Meta:model = Userfields = ('username', 'email')# 对两次输入的密码是否一致进行检查def clean_password2(self):data = self.cleaned_dataif data.get('password') == data.get('password2'):return data.get('password')else:raise forms.ValidationError("密码输入不一致,请重试。")

定义模型

app01/models.py

from django.db import modelsfrom django.contrib.auth.models import User# Create your models here.class vip_uer(models.Model):name = models.CharField(max_length=32, verbose_name='姓名')carnum = models.CharField(max_length=32, unique=True, verbose_name='车牌号')phone = models.CharField(max_length=32, verbose_name='手机号')begintime = models.DateTimeField(auto_now=False, auto_now_add=False)endtime = models.DateTimeField(auto_now=False, auto_now_add=False)user = models.OneToOneField(User, on_delete=models.CASCADE)class car_record(models.Model):carnum = models.CharField(max_length=32, verbose_name='车牌号')intime = models.DateTimeField(auto_now=False, auto_now_add=False, verbose_name='入场时间')outtime = models.DateTimeField(auto_now=False, auto_now_add=False, verbose_name='出场时间', null=True, blank=True)paytime = models.DateTimeField(auto_now=False, auto_now_add=False ,verbose_name='收费时间', null=True, blank=True)amount = models.IntegerField(verbose_name='收费金额', null=True, blank=True)class License_plate(models.Model):car_img = models.ImageField(upload_to='car_imgs',unique=True, blank=True, null=True)car_num = models.CharField(max_length=32, unique=True, verbose_name='车牌号', null=True)color = models.CharField(max_length=32, verbose_name='车牌颜色')class Charge_type(models.Model):type = models.CharField(max_length=32, verbose_name='套餐')amount = models.IntegerField(verbose_name='费用(元)', null=True, blank=True)remark = models.CharField(max_length=50, verbose_name='备注')

视图实现

调用百度云图像识别视图

详细的操作见如下链接。

手把手带你基于Python+Django+百度云实现车牌识别_车牌识别代码python-CSDN博客

app01/views.py

def Park_discern(image):APP_ID = '百度云APP_ID'API_KEY = '百度云API_KEY'SECRET_KEY = '百度云SECRET_KEY'# 创建客户端对象client = AipOcr(APP_ID, API_KEY, SECRET_KEY)# 建立连接的超时时间,单位为毫秒client.setConnectionTimeoutInMillis(5000)# 通过打开的连接传输数据的超时时间,单位为毫秒client.setSocketTimeoutInMillis(5000)res = client.licensePlate(image)return res
车辆入场视图
def car_in(request):if request.method == 'POST':# 读取图片img = request.FILES.get('car_img')if img == None:# 没有选择图片,而直接点击检测error = '请选择一张图片!'return render(request, 'car_in.html', {'error': error})else:try:# 将图片数据存起来new_car = models.License_plate.objects.create(car_img=img)print(new_car)# 定义读取图片函数def get_file_content(filePath):with open(filePath, 'rb') as fp:return fp.read()#生成图片地址url = './media/' + str(new_car.car_img)print(url)# 读取图片信息image = get_file_content(url)# 调用接口识别车牌res = Park_discern(image)#车牌号carnum = res['words_result']['number']#车牌颜色color = res['words_result']['color']try:# 车牌是否识别过is_carnum = models.License_plate.objects.get(car_num=carnum)if is_carnum:#识别过了的直接从数据库读取历史数据并删除当前存储的图片数据和文件new_car.car_img = is_carnum.car_imgprint(new_car.id)models.License_plate.objects.filter(id=new_car.id).delete()except models.License_plate.DoesNotExist:# 没识别过,则保存车牌和颜色信息new_car.color = colornew_car.car_num = carnumnew_car.save()# return redirect('carnum_add')print(new_car.car_img)return render(request,'car_in.html',{'carport_url':new_car.car_img,'carnum':carnum,'color':color})except Exception as e:return HttpResponse('识别发生错误!')return render(request, 'car_in.html',{'carport_url':'car_imgs/intro.jpg'})
车辆出场功能视图
def car_out(request):if request.method == 'POST':# 读取图片img = request.FILES.get('car_img')if img == None:# 没有选择图片,而直接点击检测error = '请选择一张图片!'return render(request, 'car_in.html', {'error': error})else:try:# 将图片数据存起来new_car = models.License_plate.objects.create(car_img=img)# 定义读取图片函数def get_file_content(filePath):with open(filePath, 'rb') as fp:return fp.read()#生成图片地址url = './media/' + str(new_car.car_img)# 读取图片信息image = get_file_content(url)# 调用接口识别车牌res = Park_discern(image)#车牌号carnum = res['words_result']['number']#车牌颜色color = res['words_result']['color']try:# 车牌是否识别过is_carnum = models.License_plate.objects.get(car_num=carnum)if is_carnum:#识别过了的直接从数据库读取历史数据并删除当前存储的图片数据和文件new_car.car_img = is_carnum.car_imgprint(new_car.id )models.License_plate.objects.filter(id=new_car.id ).delete()except models.License_plate.DoesNotExist:# 没识别过,则保存车牌和颜色信息new_car.color = colornew_car.car_num = carnumnew_car.save()# return redirect('carnum_add')print(new_car.car_img)return render(request,'car_out.html',{'carport_url':new_car.car_img,'carnum':carnum,'color':color})except Exception as e:return HttpResponse('识别发生错误!')return render(request, 'car_out.html',{'carport_url':'car_imgs/intro.jpg'})
首页(Dashboard)实现
def index(request):if request.method == 'POST':chargedays = request.POST.get('chargedays')carnum = request.POST.get('carnum')days = datetime.timedelta(days=chargedays)if models.vip_uer.objects.filter(carnum=carnum):vip = vip_uer.objects.get(carnum=carnum)endtime = vip.endtime + daysprint(vip.endtime)models.vip_uer.objects.update(endtime = endtime)else:begtime = timezone.now()endtime = begtime + daysmodels.vip_uer.objects.create(carnum=carnum, begintime = begtime ,endtime=endtime)context = "充值成功!" + "您的会员截止日期为:" + str(endtime.date())return HttpResponse(context)else:today = timezone.now().date()#当天start = today + datetime.timedelta(days=-1)#前一天end = today + datetime.timedelta(days=1)#后一天carin_today = car_record.objects.filter(intime__range = (today,end)).count()#当天入场数carin_yestoday = car_record.objects.filter(intime__range = (start,today)).count() #昨天入场数carout_today = car_record.objects.filter(outtime__range = (today,end)).count()carout_yestoday = car_record.objects.filter(outtime__range = (start,today)).count()print(today)# rate_in = 0if carin_today != 0:rate_in = (carin_today - carin_yestoday)/carin_today * 100else:rate_in = 0if rate_in <= 0 :rate_sign = 0else:rate_sign = 1if carout_today != 0:rate_out = (carout_today - carout_yestoday)/carout_today * 100# rate_out = round(rate_out,2)else:rate_out = 0if rate_out <= 0:rate_sign_o = 0else:rate_sign_o = 1datain_record = []dataout_record = []labels_date = []#当天收费金额charge_amount_today = models.car_record.objects.filter(outtime__range = (today,end)).aggregate(Sum('amount'))['amount__sum']charge_amount_yestoday = models.car_record.objects.filter(outtime__range = (start,today)).aggregate(Sum('amount'))['amount__sum']# charge_amount_today = int(charge_amount_today)if charge_amount_today == None:charge_amount_today = 0if charge_amount_yestoday == None:charge_amount_yestoday = 0print(charge_amount_yestoday)if charge_amount_today != 0:rate_amount = (charge_amount_today - charge_amount_yestoday)/charge_amount_today * 100else:rate_amount = 0if rate_amount <= 0:rate_sign_a = 0else:rate_sign_a = 1for i in range(0,20):in_record = car_record.objects.filter(intime__range = (today,end)).count()out_record = car_record.objects.filter(outtime__range = (today,end)).count()labels_date.append(today.strftime("%Y-%m-%d"))today = today + datetime.timedelta(days=-1)end = end + datetime.timedelta(days=-1)datain_record.append(in_record)dataout_record.append(out_record)dataWeb = datain_recorddataSocial = dataout_record# dataSocial = [50, 0, 0, 0, 0, 1, 1, 0, 0, 0]# dataOther = [30, 9, 1, 7, 8, 3, 6, 5, 5, 4]all_cars = models.car_record.objects.all()context = {'dataWeb':dataWeb,'dataSocial':dataSocial,'all_cars':all_cars, 'carin_today':carin_today,'carout_today':carout_today, 'rate_in':abs(rate_in),'rate_out':abs(rate_out), 'rate_sign':rate_sign,'rate_sign_o':rate_sign_o, 'labels_date':labels_date, 'charge_amount_today':charge_amount_today, 'rate_amount':rate_amount, 'rate_sign_a':abs(rate_sign_a) }return render(request, 'index.html',context)
注册登录登出视图
from django.contrib.auth import authenticate, login,logoutfrom django.http import HttpResponsefrom .forms import UserLoginForm,UserRegisterForm# Create your views here.def user_login(request):if request.method == 'POST':user_login_form = UserLoginForm(data=request.POST)if user_login_form.is_valid():# .cleaned_data 清洗出合法数据data = user_login_form.cleaned_data# 检验账号、密码是否正确匹配数据库中的某个用户# 如果均匹配则返回这个 user 对象user = authenticate(username=data['username'], password=data['password'])if user:# 将用户数据保存在 session 中,即实现了登录动作login(request, user)return redirect("index")else:return HttpResponse("账号或密码输入有误。请重新输入~")else:return HttpResponse("账号或密码输入不合法")elif request.method == 'GET':user_login_form = UserLoginForm()context = { 'form': user_login_form }return render(request, 'login.html', context)else:return HttpResponse("请使用GET或POST请求数据")def user_logout(request):logout(request)return redirect("index")# 用户注册def user_register(request):if request.method == 'POST':user_register_form = UserRegisterForm(data=request.POST)if user_register_form.is_valid():new_user = user_register_form.save(commit=False)# 设置密码new_user.set_password(user_register_form.cleaned_data['password'])new_user.save()# 保存好数据后立即登录并返回博客列表页面login(request, new_user)return redirect("index")else:return HttpResponse("注册表单输入有误。请重新输入~")elif request.method == 'GET':user_register_form = UserRegisterForm()context = { 'form': user_register_form }return render(request, 'register.html', context)else:return HttpResponse("请使用GET或POST请求数据")

模板实现

templates/base.html

{% load static %}{##}{% block title %}{% endblock %}{% include 'header.html' %}{% block content %}{% endblock content %}{% include 'footer.html' %}

templates/header.html

{% load static %}
车辆入场模板

templates/car_in.html

{% extends "base.html" %}{% load static %}{% block title %} 车辆入场 {% endblock title %}{% block content %}

入场车牌识别

{% csrf_token %}{#
<input class="form-control" type="text" name="carnum" value="{{ carnum }}"><input class="form-control" type="text" name="color" value="{{ color }}">
{##}$("#car_img").change(function () {var rd_img = new FileReader();rd_img.readAsDataURL(this.files[0]);rd_img.onload = function () {$("#imgforshow").attr("src", rd_img.result);};});{% endblock content %}

路由URL实现

from django.contrib import adminfrom django.urls import path,re_pathfrom app01 import viewsfrom django.conf import settingsfrom django.conf.urls.static import staticurlpatterns = [path('admin/', admin.site.urls),re_path(r'^$', views.index, name='index'),path('recharge/', views.recharge),path('test/', views.test),path('index/', views.index, name='index'),path('car_records/', views.car_records),path('car_in/', views.car_in),path('carin_update/', views.carin_update),path('car_out/', views.car_out),path('carout_update/', views.carout_update),path('carout_charge/', views.carout_charge),path('vip_records/', views.vip_records),path('code/', views.code),# 用户登录path('login/', views.user_login, name='login'),# 用户退出path('logout/', views.user_logout, name='logout'),# 用户注册path('register/', views.user_register, name='register'),]#添加这行urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)