对资源的增查改删处理
您需要高效学习,找工作? 点击咨询 报名实战班
点击查看学员就业情况
前面已经说过,如果采用前后端分离的架构开发, 后端几乎不负责任何展现界面的工作,只负责对数据进行管理 。
数据的管理,主要就是:响应前端的请求, 对数据资源的 增加、修改、删除、列出
。
下面我们就以 BYSMS 系统中 customer 数据为例,看看如何进行 数据的增查改删 操作。
现在我们的系统,API接口 已经由架构师定义好了, 点击这里查看。
其中包括了客户数据的 增查改删 接口。
大家先仔细阅读一下这个接口文档,将来你们可能就需要根据这样的文档来实现后端的功能哦。
现在我们就根据这个接口文档,来实现后端。
创建 mgr应用目录
接口文档明确说明了,这是针对 管理员用户 的 请求。
前面我们已经为 销售员用户 专门创建了一个应用 sales 来处理相关的 请求。
所以,我们可以 再为 管理员用户 专门创建一个应用 mgr 来处理相关的 请求。 怎么创建还记得吗?
对了,执行
添加处理请求模块 和 url 路由
前面,我们都是在views.py 里面定义函数,处理 http请求的。
但是可以想象, 以后,这个mgr应用要处理很多类型的http请求。
都用这个views.py 就会让这个文件非常的庞大, 不好维护。所以,我们可以用不同的 py 文件处理不同类型的http请求。
比如,这里我们可以新增一个文件 customer.py, 专门处理 客户端对 customer 数据的操作。
将来如果客户端有对其他类型数据的操作, 比如 order 数据, 我们就可以添加 orders.py 来处理。
接下来,从接口文档,我们可以发现对资源的增删改查 操作, 都是同一个URL,都是 /api/mgr/customers
。
而且我们发现,不同的操作请求,使用不同的 HTTP 请求方法 ,比如 添加是POST, 查询是 GET, 修改是 PUT, 删除是 DELETE。
而且请求的参数中都有 action 参数表明这次请求的操作具体是什么。
注意:Django 的 url路由功能 不支持 根据 HTTP 请求的方法 和请求体里面的参数 进行路由。
就是不能像下面这样,来根据请求 是 post 还是 get 来 路由
path('customers/', 'app.views.list_customer', method='get'),
path('customers/', 'app.views.add_customer', method='post'),
那么大家想想该怎么办?
一种方式是:自己编写一个函数, 来 根据 http请求的类型 和请求体里面的参数 分发(或者说路由)给 不同的函数进行处理。
我们可以 在 customer.py 中定义如下 dispatcher 函数
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请求'})
该函数 把 请求消息中的参数统一放入到 request请求对象的params 属性中。
params 属性 被 做成一个 dict 类型 , 方便后面的处理函数来获取消息中的参数。
然后 dispatch函数再根据 请求的 类型 和 action 参数的值 决定由那个函数具体处理该请求消息。
比如 action 参数 为 'add_customer' 的 请求 就由 addcustomer 函数 进行处理。
当然在文件的开头,我们需要 先导入 JsonResponse 和 json 的定义,像下面这样
接下来,根据 API 接口 ,我们发现 凡是 API 请求url为 /api/mgr/customers
的,都属于 客户 相关的API, 都应该交由 我们上面定义的dispatch函数进行分派处理。
那么我们需要在Django的url路由文件中加入对应的路由。
所以,
第一步:我们应该在 总路由文件 bysms/urls.py
中定义了如下部分
第二步: 在 mgr 目录下面添加 urls.py 路由文件, 并 加入如下声明即可, 如下所示
from django.urls import path
from mgr import customer
urlpatterns = [
path('customers', customer.dispatcher),
]
这样,就表示 凡是 API 请求url为 /api/mgr/customers
的,都交由 我们上面定义的dispatch函数进行分派处理
列出客户
通常数据资源的 增查改删 里面的 查 就是 查看,对应的就是列出数据资源。
根据接口文档,列出客户数据接口,后端返回的数据格式如下
{
"ret": 0,
"retlist": [
{
"address": "江苏省常州武进市白云街44号",
"id": 1,
"name": "武进市 袁腾飞",
"phonenumber": "13886666666"
},
{
"address": "北京海淀区",
"id": 4,
"name": "北京海淀区代理 蔡国庆",
"phonenumber": "13990123456"
}
]
}
看到没有,这里我们无需 将数据库中获取的数据 转化为 供浏览器展示的HTML。
在前后端分离的开发架构中,如何展示数据,那是前端的事情。
我们后端只需要根据接口文档, 返回原始数据就行。
我们可以使用如下的函数来返回数据库的所有的 客户数据信息
def listcustomers(request):
# 返回一个 QuerySet 对象 ,包含所有的表记录
qs = Customer.objects.values()
# 将 QuerySet 对象 转化为 list 类型
# 否则不能 被 转化为 JSON 字符串
retlist = list(qs)
return JsonResponse({'ret': 0, 'retlist': retlist})
当然在文件的开头,我们需要 先导入 Customer 定义,像下面这样
可以发现,无需转化数据为HTML, 后端的代码任务也大大减轻。
添加客户
通常数据资源的 增查改删 里面的 增 就是 添加,对应的就是添加数据资源。
根据接口文档,添加客户数据接口,前端提供的客户数据格式如下
{
"action":"add_customer",
"data":{
"name":"武汉市桥西医院",
"phonenumber":"13345679934",
"address":"武汉市桥西医院北路"
}
}
我们可以使用如下的函数来处理
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})
Customer.objects.create
方法就可以添加一条Customer表里面的记录。
临时取消 CSRF 校验
根据接口文档,添加客户 请求是个Post请求
注意,新创建的项目, Django 缺省会启用一个 CSRF (跨站请求伪造) 安全防护机制。
在这种情况下, 所有的Post、PUT 类型的 请求都必须在HTTP请求头中携带用于校验的数据。
为了简单起见,我们先临时取消掉CSRF的 校验机制,等以后有需要再打开。
要临时取消掉CSRF的 校验机制,非常简单,只需要在 项目的配置文件 bysms/settings.py
中 MIDDLEWARE
配置项 里 注释掉 'django.middleware.csrf.CsrfViewMiddleware' 即可。
如下所示
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',
]
修改客户信息
数据资源的 增查改删 里面的 改 就是 改动,对应的就是修改数据资源。
根据接口文档,修改客户数据接口,前端提供的数据格式如下
{
"action":"modify_customer",
"id": 6,
"newdata":{
"name":"武汉市桥北医院",
"phonenumber":"13345678888",
"address":"武汉市桥北医院北路"
}
}
我们可以使用如下的函数来处理
def modifycustomer(request):
# 从请求消息中 获取修改客户的信息
# 找到该客户,并且进行修改操作
customerid = request.params['id']
newdata = request.params['newdata']
try:
# 根据 id 从数据库中找到相应的客户记录
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']
# 注意,一定要执行save才能将修改信息保存到数据库
customer.save()
return JsonResponse({'ret': 0})
删除客户
数据资源的 增查改删 里面的 删 就是 删除,对应的就是删除数据资源。
根据接口文档,删除客户数据接口,前端只需要提供要删除的客户的ID。
数据格式如下
我们可以使用如下的函数来处理
def deletecustomer(request):
customerid = request.params['id']
try:
# 根据 id 从数据库中找到相应的客户记录
customer = Customer.objects.get(id=customerid)
except Customer.DoesNotExist:
return {
'ret': 1,
'msg': f'id 为`{customerid}`的客户不存在'
}
# delete 方法就将该记录从数据库中删除了
customer.delete()
return JsonResponse({'ret': 0})
测试后端代码
后端对 客户数据的增查改删已经实现了。
那么,怎么测试我们的代码是否正确呢?
有的同学说,可以等前端开发好了对接。
但是, 我们后端的开发和前端开发是并行的,要等前端开发好了,你们现在干什么?
喝茶看报纸吗?老板肯定不答应。
测试我们的代码,可以自己开发测试程序,模拟前端,发出http请求 对 后端进行测试。
这就是 Web API 接口测试了, Python做接口测试, 最合适的就是使用 requests 这个库。
这里,我们只需要 使用 requests库构建 各种 接口规定的 http 请求消息, 并且检查响应。
比如,要检查 列出客户 的接口
import requests,pprint
response = requests.get('http://localhost/api/mgr/customers?action=list_customer')
pprint.pprint(response.json())
运行一下,大家看看,是不是可以返回这样的结果呢?
{'ret': 0,
'retlist': [{'address': '梁山行者武松大寨',
'id': 1,
'name': '武松',
'phonenumber': '14456789012',
'qq': '23434344'},
{'address': '梁山黑旋风大寨',
'id': 2,
'name': '李逵',
'phonenumber': '13000000001',
'qq': '234234234324'}]}
根据接口文档,ret 值为0,表示 接口调用成功。
如果retlist里面的格式符合接口规定,并且其中的数据和数据库内容一致,则测试通过
要检查添加客户的接口,如下
import requests,pprint
# 构建添加 客户信息的 消息体,是json格式
payload = {
"action":"add_customer",
"data":{
"name":"武汉市桥西医院",
"phonenumber":"13345679934",
"address":"武汉市桥西医院北路"
}
}
# 发送请求给web服务
response = requests.post('http://localhost/api/mgr/customers',
json=payload)
pprint.pprint(response.json())
# 构建查看 客户信息的消息体
response = requests.get('http://localhost/api/mgr/customers?action=list_customer')
# 发送请求给web服务
pprint.pprint(response.json())
和前端集成
最终我们的产品 前端和后端系统会集成在一起成为一个完整的系统。
部署到生产环境(生产环境就是正式的线上运营环境)运行的架构往往比较复杂。我们在后面有专门的章节讲述 一个比较完整的线上环境 如何搭建。
这里我们讲述开发环境下, 前后端分离的架构如何简单集成。
前端环境其实就是 一些前端的代码和资源文件,包括 js文件、html文件、css文件 还有 图片视频文件等。
我们模拟前端团队开发的 前端 系统 打包在这里 ,点击这里下载
下载好以后,可以解压该 z_dist.zip 文件到项目根目录下面,形成一个目录 z_dist。
该目录下面就是前端的 代码资源文件。
Django的开发环境也可以从浏览器访问这些前端的资源文件。
但是前端文件都是静态文件,需要我们配置一下Django的配置文件, 指定http请求如果访问静态文件,Django在哪个目录下查找。
注意,接下来我们配置 Django 静态文件服务, 是 开发时
使用的 一种 临时方案
,性能很低,这是方便我们调试程序用的。
前面讲过,正式部署web服务的时候,不应该这样干,应该采用其它方法,比如Nginx等。后面的教程会有详细的讲解如何使用Nginx 和 Django 组合使用。
现在,请打开 bysms/urls.py
文件,在末尾 添加一个
并添加如下声明
最终,内容如下
from django.contrib import admin
# 导入一个include函数
from django.urls import path, include
# 静态文件服务
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
# 凡是 url 以 sales/ 开头的,
# 都根据 sales.urls 里面的 子路由表进行路由
path('sales/', include('sales.urls')),
# 凡是 url 以 api/mgr 开头的,
# 都根据 mgr.urls 里面的 子路由表进行路由
path('api/mgr/', include('mgr.urls')),
] + static("/", document_root="./z_dist")
最后的
就是在url 路由中加入 前端静态文件的查找路径。
这样如果 http请求的url 不是以 admin/ sales/ api/mgr/ 开头, Django 就会认为是要访问 z_dist目录下面的静态文件。
好了,现在我们 运行如下命令,启动Django 开发服务器
然后我们打开浏览器,输入如下网址:
就会出现 管理员操作界面,如下
这是前端开发的 客户管理界面,可以在界面上进行客户的 增查改删操作, 这些操作会触发API 请求发送给我们的后端服务。
大家可以操作一下看看, 后端是否能够正确的响应。
目前为止,我们项目代码,在如下百度网盘中的 bysms_05.zip
百度网盘链接:https://pan.baidu.com/s/1nUyxvq6IYykBNtPUf4Ho6w 提取码:9w2u
学员常见问题
不少朋友反应,下载示例代码,运行后,发现页面是一片空白, 按F12发现如下错误
Refused to execute script from 'http://localhost/mgr/index.js?...'
because its MIME type ('text/plain') is not executable,
and strick MIME type checking is enabled.
那是因为你们的 Windows系统上不知道什么时候破坏了注册表,把扩展名为js的文件类型注册为 'text/plain', 应该为'application/javascript'。
要解决这个问题,请在 Django项目 setting.py 中 末尾添加如下代码,强制Django使用'application/javascript' 作为 js文件类型
import mimetypes
mimetypes.add_type('text/css', '.css')
mimetypes.add_type('application/javascript', '.js')
然后,刷新网页(按ctrl键刷新,确保不用缓存)即可。