Django项目:订单管理系统

1.Django简介

Django是一款开源的python web应用框架,采用MTV(Model-Template-View)模式,其中Model负责对象与数据库的映射(ORM),Template通过接口等方式将前端页面传递给用户,View视图函数对应业务逻辑,在合适的时候调用Model或Template

除去MTV模型以外,Django还有一个URL控制器,分别将不同的URL请求传递给不同的View视图函数处理,再通过各自的View函数来调用对应的Model和Template

2.Django项目:订单管理系统

1.Django项目的创建

在python 3中安装完成Django库后,可以在CMD或Pycharm终端里执行:django-admin startproject itemproj来创建Django项目

也可以在Pycharm专业版本直接创建一个Django项目,这样会自动生成一个空的Template文件夹,以及在settings.py中生成Template对应的路径。还可以直接创建初始APP,这里没有做

(本文使用的Django版本为3.2.4)

Django项目创建
执行创建完成后,系统会为我们生成这样一个初始目录:

Django目录
其中:

· manage.py是Django项目管理的工具脚本,执行Django命令时通过这个文件进行管理操作

· itemproj/settings.py是Django项目的配置文件,包括了App配置、Django中间件、数据库等重要设置,在实际开发中要经常修改其中内容

· itemproj/urls.py是URL路由映射文件,其中声明了前端发送的各种http请求,再通过各级路由文件来寻找到对应的View视图函数来进行处理

· itemproj/wsgi.py,WSGI是Python web服务网关接口规范(Web Server Gateway Interface)的简称,由wsgi web server和wsgi web application两部分组成,是运行在同一个python进程中的两个模块

wsgi web sever接收了http请求以后调用wsgi web application接口,处理请求的具体内容,处理完成后再将结果返回给wsgi web server,通过wsgi web server将请求传递给前端,如图所示(Django官方文档):

WSGI
· itemproj/asgi.py,ASGI即异步网关协议接口(Asynchronous Server Gateway Interface),是一个介于网络协议服务和python应用之间的标准接口,用于处理通用类型的协议。参考自Django官方文档的资料,ASGI是为了支持异步网络服务器和应用而出现的新的python标准,可以将ASGI理解为WSGI协议的扩展

ASGI

2.Django生命周期

了解了Django项目基本组成后,Django的生命周期也基本明朗:前端发起URL请求WSGIDjango中间件URL路由View视图函数Model数据库交互Template模板进行html渲染Django中间件WSGI前端页面

3.Django项目启动

通过在Pycharm终端中执行python manage.py runserver来启动Django服务,系统默认在:8000端口上执行

Django启动
可以在settings.py中增加ALLOWED_HOSTS的参数,来增加支持的hosts,这里新增一个localhost。也可以自定义端口号等

1
ALLOWED_HOSTS = ['localhost']

4.创建Django APP

首先创建一个app专门负责处理订单管理系统中对订单销售的增删改查请求

在Pycharm终端中执行python manage.py startapp sales,会创建一个名为sales的app目录,其中包含新的下列文件:

sales目录
该文件目录其实就是一个python包

5.编辑View视图函数和URL路由

打开sales/views.py文件,准备新增一条视图功能:

1
2
3
4
from django.http import HttpResponse

def listorders(request):
return HttpResponse("下面是系统中所有的订单信息......")

这里将返回HttpResponse对象的字符串参数

接着打开itemproj/urls.py文件,导入视图函数listordes,在列表变量urlpatterns中新增指向View的路由信息。urlpatterns就是Django URL路由的主入口

1
2
3
4
5
6
7
8
9
from django.contrib import admin
from django.urls import path

from sales.views import listorders

urlpatterns = [
path('admin/', admin.site.urls),
path('sales/orders/', listorders),
]

启动Django

订单信息
在实际工程项目中,系统的urls条目复杂多变,放在同一份目录下会显得臃肿且难以维护,查询起来非常困难,不符合后端项目的设计理念,这里就需要将系统的urls做成树形系统,按照urls对应的视图功能将其拆分进对应的URL路由子表当中

比如,这里将所有以sales开头的urls全部放在sales app目录下新创建的子路由sales/urls.py文件中

1
2
3
4
5
6
7
from django.urls import path

from . import views

urlpatterns = [
path('orders/', views.listorders),
]

再对主路由文件itemproj/urls.py进行修改

1
2
3
4
5
6
7
8
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
path('admin/', admin.site.urls),

path('sales/', include('sales.urls')),
]

6.创建数据库

数据库自然是Django后端开发中的重要内容,Django对其的设置保存在itemproj/settings.py文件中

进入settings.py,找到settings.pyDATABASES设置

1
2
3
4
5
6
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}

可以看到Django默认的数据库引擎为sqlite3,要添加MySql数据库,可以将其设置为:

1
2
3
4
5
6
7
8
9
10
DATABASES = {
'default':{
'ENGINE':'django.db.backends.mysql',
'NAME':'testdemo1', #所使用的数据库的名字
'USER':'root', #数据库用户
'PASSWORD':'123456', #密码
'HOST':'127.0.0.1', #主机
'PORT':'3306', #端口
}
}

执行创建本地数据库的指令python manage.py migrate

默认情况下就会在项目根目录下生成一个db.sqlite3,并在其中自动创建一些Django表

创建本地数据库
可以在Navicat中将其打开

数据库表

7.创建表单

新建一份common app应用目录,在其中存放一些公共信息

python manage.py startapp common

进入app中common/models.py,添加内容:

1
2
3
4
5
6
7
8
9
10
11
from django.db import models

class Customer(models.Model):
# 客户名称
name = models.CharField(max_length=200)

# 联系电话
phonenumber = models.CharField(max_length=200)

# 邮箱
address = models.CharField(max_length=200)

其中的客户Customer类继承自django.db.models.Model,用来定义数据库表格,添加的namephonenumberadress分别是表中的3个varchar字段,参数max_length则限制了3个字段的最大长度

接着来到itemproj/settings.py中,添加common app

1
2
3
4
5
6
7
8
9
10
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',

'common.apps.CommonConfig',
]

完成后,Django已经真正添加了app用,继续执行命令python manage.py makemigrations common记录对数据库的操作

执行完成后,会在common/migrations.py目录下生成0001_inital.py文件,该脚本就是进行的数据库操作代码

记录数据库操作
0001_inital.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from django.db import migrations, models

class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='Customer',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('phonenumber', models.CharField(max_length=200)),
('address', models.CharField(max_length=200)),
],
),
]

接下来执行python manage.py migrate去数据库中创建表

创建表1
创建表1

8.创建Django管理员

Django提供了一个内置的管理员操作页面,可以直接进行对数据库的增删改查工作,将数据库操作变得简单。

首先创建超级管理员账号,执行命令python manage.py createsuperuser

因为是做测试学习使用,所以密码设置得相对简单,直接无视掉警告,同样的,还可以临时取消Django的CSRF(跨站请求伪造)校验,否则后续的HTTP请求都必须携带校验数据

创建superuser

1
2
3
4
5
6
7
8
9
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
#'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

要牢记设置的密码,因为忘记密码后如果想要去数据库里查看,不经过密码学解密的情况下,密码在数据表中是以pbkdf2_sha256算法加密后的密文形式记录的,这在Django后台管理页面当中也会有提示

pbkdf2_sha256加密1
pbkdf2_sha256加密2
如果担心忘记密码可以先将密码注释到itemproj/settings.py中

1
2
3
4
5
6
'''
Username (leave blank to use 'elbadaernu9.9'): 9.9
Email address: rtl1312@163.com
Password: 00000000
Superuser created successfully.
'''

然后修改common应用中管理员配置文件common/admin.py,注册自定义的model类,提交给Django

1
2
3
4
5
from django.contrib import admin

from .models import Customer

admin.site.register(Customer)

启动Django,访问http://localhost:8000/admin,出现管理员登陆界面

superuser登陆
登陆后进入到管理员后台

管理员后台
增加客户数据

新增数据1
新增数据2
上述步骤完成以后,进入Navicat,刷新common_customer表,可以看到新增数据已经出现在数据库当中

新增数据3
要将管理员界面设置为中文,可以到到itemproj/settings.py中对中间件加入如下设置:

1
2
3
4
5
6
7
8
9
10
11
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
#管理员后台界面设置为中文
'django.middleware.locale.LocaleMiddleware',
]

9.获取数据

先来实现第一个功能,当浏览器访问/sales/customs/时,服务器会返回系统中的所有客户记录

来到sales/views.py中,定义listcustomers函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from  common.models import  Customer

def listcustomers(request):
qs = Customer.objects.values()

# 定义返回字符串
retStr = ''
for customer in qs:
for name,value in customer.items():
retStr += f'{name} : {value} | '

retStr += '<br>'

return HttpResponse(retStr)

Customer.objects.values()会返回Django定义的QuerySet对象,将返回包含的全部Customer表记录,每条记录都是一个字典对象

接着添加对/sales/customs/访问url请求的路由

1
2
3
4
5
6
7
8
9
10
from django.urls import path

from . import views

urlpatterns = [
path('orders/', views.listorders),

#这里是新添加的路由信息
path('customers/', views.listcustomers),
]

启动Django,进入对应页面后出现:

customers
因为在实际订单信息中,客户数量庞大,可以对代码新增过滤条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def listcustomers(request):
qs = Customer.objects.values()

# 检查url中是否有参数phonenumber
ph = request.GET.get('phonenumber',None)

if ph:
qs = qs.filter(phonenumber=ph)

# 定义返回字符串
retStr = ''
for customer in qs:
for name,value in customer.items():
retStr += f'{name} : {value} | '
retStr += '<br>'

return HttpResponse(retStr)

过滤客户

10.使用Html展示数据

使用Django内置的模板引擎,定义一段Html代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
html_template = '''
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
table {
border-collapse: collapse;
}
th, td {
padding: 8px;
text-align: left;
border-bottom: 1px solid #ddd;
}
</style>
</head>
<body>
<table>
<tr>
<th>id</th>
<th>姓名</th>
<th>电话号码</th>
<th>邮箱</th>
</tr>

{% for customer in customers %}
<tr>

{% for name, value in customer.items %}
<td>{{ value }}</td>
{% endfor %}

</tr>
{% endfor %}

</table>
</body>
</html>
'''

from django.template import engines

django_engine = engines['django']
template = django_engine.from_string(html_template)

def listcustomers(request):
qs = Customer.objects.values()

ph = request.GET.get('phonenumber', None)

if ph:
qs = qs.filter(phonenumber=ph)

# 传入渲染模板需要的参数
rendered = template.render({'customers': qs})

return HttpResponse(rendered)

Html渲染数据
在实际前后端分离的开发中,Html部分的工作通常交由前端开发人员,后端主要是准备好与前端页面的API接口工作

11.对数据的增删改查

1.创建管理应用

针对管理员用户的工作,继续创建新应用mgr来处理管相关请求

创建mgr app
为了避免面向mgr管理员的系统里mgr/views.py中的函数过于庞大,新创建一份mgr/customer.py来专门处理对customer数据的操作

2.设置URL路由

实际的增删改查操作都发生在同一份路由当中

· GET:获取请求

· POST:添加请求

· PUT:修改请求

· DELETE:删除请求

而Django不支持在路由当中添加HTTP请求方法,可以在mgr/customer.py文件中新增函数dispatcher()来管理请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from django.http import JsonResponse
import json

def dispatcher(request):
# 将请求参数统一放入request的params属性中,方便后续处理

# GET请求 参数在url中,通过request对象的GET属性获取
if request.method == 'GET':
request.params = request.GET

# POST/PUT/DELETE 请求 参数从request对象的body属性中获取
elif request.method in ['POST','PUT','DELETE']:
# 接口中POST/PUT/DELETE请求的消息体都是json格式
request.params = json.loads(request.body)

# 根据不同的action分派给不同的函数进行处理
action = request.params['action']
if action == 'list_customer':
return listcustomers(request)
elif action == 'add_customer':
return addcustomer(request)
elif action == 'modify_customer':
return modifycustomer(request)
elif action == 'del_customer':
return deletecustomer(request)
else:
return JsonResponse({'ret': 1, 'msg': '不支持该类型http请求'})

进入主路由itemproj/urls.py新增

1
path('api/mgr/', include('mgr.urls')),

然后创建并进入子路由mgr/urls.py添加路径声明

1
2
3
4
5
6
7
from django.urls import path
from mgr import customer

urlpatterns = [

path('customers', customer.dispatcher),
]

这样,如果有来自API请求/api/mgr/customers当中内容,都将经过dispatcher()函数处理

继续完成增删改查功能对应的函数

3.增加客户

导入

1
from common.models import Customer

1
2
3
4
5
6
7
8
9
def addcustomer(request):

info = request.params['data']

record = Customer.objects.create(name=info['name'] ,
phonenumber=info['phonenumber'] ,
address=info['address'])

return JsonResponse({'ret': 0, 'id':record.id})

4.删除客户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def deletecustomer(request):

customerid = request.params['id']

try:
customer = Customer.objects.get(id=customerid)
except Customer.DoesNotExist:
return {
'ret': 1,
'msg': f'id 为`{customerid}`的客户不存在'
}

customer.delete()

return JsonResponse({'ret': 0})

5.修改客户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def modifycustomer(request):

# 从请求消息中 获取修改客户的信息
# 找到该客户,并且进行修改操作

customerid = request.params['id']
newdata = request.params['newdata']

try:
customer = Customer.objects.get(id=customerid)
except Customer.DoesNotExist:
return {
'ret': 1,
'msg': f'id 为`{customerid}`的客户不存在'
}

if 'name' in newdata:
customer.name = newdata['name']
if 'phonenumber' in newdata:
customer.phonenumber = newdata['phonenumber']
if 'address' in newdata:
customer.address = newdata['address']

customer.save()

return JsonResponse({'ret': 0})

6.查询客户

1
2
3
4
5
6
7
8
def listcustomers(request):
# 返回一个QuerySet对象
qs = Customer.objects.values()

# 将 QuerySet对象转化为list类型,JSON字符串
retlist = list(qs)

return JsonResponse({'ret': 0, 'retlist': retlist})

12.测试

实际开发过程中可能不能及时收到前端的Html文件,此时就需要对项目功能进行一定测试

可以通过模拟前端的形式,发出HTTP请求,对后端接口进行测试

在项目根目录下新建test文件夹用于存放测试文件,并新建一份test01.py文件用于测试customer数据

导入python的requests库,做接口测试

1
2
3
4
5
import  requests,pprint

response = requests.get('http://localhost:8000/api/mgr/customers?action=list_customer')

pprint.pprint(response.json())

查询测试
继续创建test_add.py文件用于测试新增用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import  requests,pprint

payload = {
"action":"add_customer",
"data":{
"name":"小明",
"phonenumber":"12345678910",
"address":"xiaoming@163.com"
}
}

response = requests.post('http://localhost:8000/api/mgr/customers',json=payload)

pprint.pprint(response.json())

response = requests.get('http://localhost:8000/api/mgr/customers?action=list_customer')

pprint.pprint(response.json())

新增测试
此时数据库中也随之出现了“小明”的记录

数据库新增数据

13.与前端集成

制作前端的Html文件,打包后命名为front_end作为静态文件放进项目的根目录,进入itemproj/urls.py文件中,添加静态文件声明,并在末尾增加代码

1
2
3
4
5
6
7
8
9
10
from django.contrib import admin
from django.urls import path, include
from django.conf.urls.static import static

urlpatterns = [
path('admin/', admin.site.urls),

path('sales/', include('sales.urls')),
path('api/mgr/', include('mgr.urls')),
] + static("/", document_root="./front_end")

最后添加的就是查找前端静态文件的URL路由,一但HTTP请求不是以admin/ sales/ api/开头,Django会访问前端目录下的静态文件

假设与前端文件的集成无误,启动Django,访问http://localhost:8000/mgr/index.html,进入管理员后台,会得到类似这样的成果

后台页面

14.登陆功能

要实现订单管理员的登陆登出功能,继续在mgr文件夹中新建一份sign_in_out.py文件,管理登陆登出

Django内置的app django.contrib.auth已经实现了登陆验证功能,并且在数据库中定义过一张auth_user表,可以直接使用其方法

auth_user

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from django.http import JsonResponse
from django.contrib.auth import authenticate, login, logout

# 登录处理
def signin(request):
# 从 HTTP POST请求中获取用户名、密码参数
userName = request.POST.get('username')
passWord = request.POST.get('password')

# 使用Django auth库里的方法校验用户名、密码
user = authenticate(username=userName, password=passWord)

# 如果能找到用户,并且密码正确
if user is not None:
if user.is_active:
if user.is_superuser:
login(request, user)
# 在session中存入用户类型
request.session['usertype'] = 'mgr'

return JsonResponse({'ret': 0})
else:
return JsonResponse({'ret': 1, 'msg': '请使用管理员账户登录'})
else:
return JsonResponse({'ret': 0, 'msg': '用户已经被禁用'})

# 否则用户名、密码有误
else:
return JsonResponse({'ret': 1, 'msg': '用户名或者密码错误'})

# 登出处理
def signout(request):
# 使用登出方法
logout(request)
return JsonResponse({'ret': 0})

再到mgr/urls.py中添加url路由

1
2
3
4
5
6
7
8
9
10
from django.urls import path
from mgr import customer
from mgr import sign_in_out

urlpatterns = [

path('customers', customer.dispatcher),
path('signin', sign_in_out.signin),
path('signout', sign_in_out.signout),
]

到这里登陆功能已经写好,对其进行测试,在test文件夹里新建测试文件test_login.py

1
2
3
4
5
6
7
8
9
10
import  requests,pprint

payload = {
'username': '9.9',
'password': '00000000'
}

response = requests.post('http://localhost:8000/api/mgr/signin',data=payload)

pprint.pprint(response.json())

如果返回{‘ret’: 0},表示成功登陆

测试登陆成功
如果登陆失败,则返回{‘ret’: 1},并指明失败原因

测试登陆失败
继续在Django管理员后台增加新的访客用户

新增非管理员用户1
新增非管理员用户2
到test文件夹里再新建测试文件test_user.py测试普通访客登录

1
2
3
4
5
6
7
8
9
10
import  requests,pprint

payload = {
'username': 'guest',
'password': 'rtl1312693017'
}

response = requests.post('http://localhost:8000/api/mgr/signin',data=payload)

pprint.pprint(response.json())

系统会提示没有权限访问

测试非管理员用户登陆
最后再通过与前端文件进行集成,得到优化后的登陆界面:

系统登陆界面
成功登陆后将跳转到之前的客户订单管理系统

Powered by Hexo and Hexo-theme-hiker

Copyright © 2017 - 2024 青域 All Rights Reserved.

UV : | PV :