我们在13章节里通过监控界面讲了如何使用jquery的动态加载数据写法,通过简单案例来说明了如何实现动态的刷新监控界面的数据,本章我们将演示如何从Django模板加载数据逐步演化到前后端分离的异步数据加载和前端渲染主流开发方式,从而进一步实现前后端的解耦,提高Django开发Web应用的灵活性。
1.1. 修改任务列表模板
目前我们显示的任务列表是采用Django后台模板的方式加载数据的,要使用JQuery,同样采用探索编程方式,先实现前端加载模拟数据,Django模板只负责加载页面的基础架构,数据的渲染交由前端JQuery js脚本来实现加载模拟数据。
1.1.1. 修改tasks.html模板代码
<!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta charset="utf-8" /> <title>任务列表</title> </head> <body> <table id="id_task_table"> <tr> <th>ID</th> <th>任务号</th> <th>源地址</th> <th>目标地址</th> <th>条码</th> <th>状态</th> <th>优先级</th> <th>开始时间</th> <th>结束时间</th> <th>作业数量</th> <th>操作</th> </tr> </table> </body> </html>
现在运行服务器,访问URL结果如下:
模板只只负责搭建要给页面的框架,数据通过JQuery来前端渲染。 页面的任务实例数据没有加载了,只显示了表格标题头。现在我们用JQuery脚本先给表格加载前端模式数据,先实现显示效果不变,逐步实现技术的切换。
1.1.2. JQuery动态添加任务列表行数据
JQuery脚本渲染代码如下:
<!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta charset="utf-8" /> <title>任务列表</title> <link href="https://cdn.bootcss.com/twitter-bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> </head> <body> <table id="id_task_table"> <tr> <th>ID</th> <th>任务号</th> <th>源地址</th> <th>目标地址</th> <th>条码</th> <th>状态</th> <th>优先级</th> <th>开始时间</th> <th>结束时间</th> <th>作业数量</th> <th>操作</th> </tr> </table> <script> var row=""; row +="<tr>"; row +="<td>1</td>"; row +="<td>100</td>"; row +="<td>103</td>"; row +="<td>05-01-01</td>"; row += "<td>101001001008</td>"; row += "<td>未处理</td>"; row += "<td>正常</td>"; row += "<td>-</td>"; row += "<td>-</td>"; row += "<td>2021-01-26 05:58:28</td>"; row += "<td>0</td>"; row += "<td><a id='1-decompose' href='1/decompose/'>分解</a> <a id='1-start' href='1/start/'>下达</a> <a id='1-change' href='1/change/'>修改</a></td>"; row +="</tr>"; $("#id_task_table tbody").append(row); </script> </body> </html>
运行效果,我们发现页面渲染结果增加一行我们模拟添加的行任务数据。这样基于JQuery动态添加任务行的基本效果就实现了,下一步需要做的就是如何通过JQuery异步调用从后台获取数据后再通过上面的脚本实现任务数据在列表的渲染。
1.2. 改造任务后台
页面端ajax异步加载任务数据,需要后台能够返回json格式封装的任务数据。同理,采用渐进式方式先直接返回模拟多个任务的json格式任务数据url taskGetList。
1.2.1. taskGetList url
文件Task/urls 增加taskGetList url,对应Task/views文件增加taskGetList 函数,代码如下:
文件:Task/urls.py
from django.urls import path,re_path from Task import views urlpatterns = [ path('', views.view_list,name='view_list'), re_path('^(?P<pk>\d+)/start/$',views.start,name='start'),#① re_path('^(?P<pk>\d+)/change/$',views.change,name='change'),#② re_path('^(?P<pk>\d+)/decompose/$',views.decompose,name='decompose'), path('taskGetList/', views.taskGetList,name='taskGetList'), ]
文件:Task/views.py
from django.http import JsonResponse ... def taskGetList(request): tasks=[] task= {'TaskId':1,'TaskNum':100,'Source':'103','Target':'05-01-01','Barcode':'101001001008','State':1,'Priority':1,\ 'BeginDate':None,'EndDate':None,'SystemDate':'2021-01-26 05:58',} tasks.append(task) tasks.append({'TaskId':2,'TaskNum':101,'Source':'102','Target':'05-01-11','Barcode':'101001001009','State':1,'Priority':1,\ 'BeginDate':None,'EndDate':None,'SystemDate':'2021-01-26 06:00',}) data={"rows":tasks,'total':len(tasks),'success':True} #① return JsonResponse( data); #②
标注①:返回数据json格式封装,rows具体的任务List,total 返回总长度,sucess 是否成功。标注②:采用JsonResponse响应方式返回。
运行结果如下:http://localhost:8080/task/taskGetList/
1.3. 再次重构页面代码
Django开发后端url服务是不是也相当的简单,好的,接下来我们就可以前台通过ajax访问这个url服务获取数据并渲染到页面了。参考1章的调用方式代码如下:
<script> //每次页面加载,ajax请求获取任务列表数据 getData() function getData() { //异步从后台获得值 $.ajax({ url: "/task/taskGetList/", success: function (result) { //d = JSON.parse(result); d = result for (const task of d.rows) { var row=""; row +="<tr>"; row +="<td>"+task.TaskId+"</td>"; row +="<td>"+task.TaskNum+"</td>"; row +="<td>"+task.Source+"</td>"; row +="<td>"+task.Target+"</td>"; row += "<td>"+task.Barcode+"</td>"; row += "<td>"+task.State+"</td>"; row += "<td>"+task.Priority+"</td>"; row += "<td>"+task.BeginDate+"</td>"; row += "<td>"+task.EndDate+"</td>"; row += "<td>"+task.SystemDate+"</td>"; row += "<td>"+task.JobCount+"</td>"; row += "<td><a id='" + task.TaskId + "-decompose' href='"+task.TaskId +"/decompose/'>分解</a> <a id='"+task.TaskId +"-start' href='"+task.TaskId +"/start/'>下达</a> <a id='"+task.TaskId +"-change' href='"+task.TaskId +"/change/'>修改</a></td>"; row +="</tr>"; $("#id_task_table tbody").append(row); } }}); } </script>
运行效果,页面渲染结果采用了接口返回的数据。
1.4. 返回model层数据
本章节到这一步就是通过model层返回数据库表里的数据了,也就是如何model list系列化为字典格式,这里我们先采用model_to_dict实现model数据字典化,然后通过JsonResponse返回json格式数据。
from django.forms.models import model_to_dict ... def taskGetList(request): tasks = [] taskList=Task.objects.all() for model in taskList: modelJson = model_to_dict(model) modelJson['SystemDate'] = model.SystemDate #① modelJson['JobCount'] =model.job_set.count() #② tasks.append(modelJson) data={"rows":tasks,'total':len(tasks),'success':True} return JsonResponse( data);
标注①:model_to_dict不会转换有默认值的属,人工直接转换。标注②:python django笔者用起来很爽很爽的一点啊,可以方便的动态属性。
现在run一下http://localhost:8080/task/taskGetList/ 结果变成了这样:
改进一下日期格式,采用格式化函数格式化日期。
from django.forms.models import model_to_dict ... def taskGetList(request): tasks = [] taskList=Task.objects.all() for model in taskList: modelJson = model_to_dict(model) modelJson['SystemDate'] = model.SystemDate.strftime('%Y-%m-%d %H:%M:%S') #① modelJson['JobCount'] =model.job_set.count() tasks.append(modelJson) data={"rows":tasks,'total':len(tasks),'success':True} return JsonResponse( data);
标注①:采用strftime('%Y-%m-%d %H:%M:%S')把日期显示为24小时格式的。
现场运行开发服务,访问刷新http://localhost:8080/task/ 我们得到了代码重构前得效果,任务列表返回了数据库表的数据,但是一些字段没显示对照的中文函数
新版本
旧版本
进一步完善的服务端代码:
from django.forms.models import model_to_dict def taskGetList(request): tasks = [] taskList=Task.objects.all() for model in taskList: modelJson = model_to_dict(model) modelJson['SystemDate'] = model.SystemDate.strftime('%Y-%m-%d %H:%M:%S') modelJson['JobCount'] =model.JobCount() #① modelJson['Priority'] =model.get_Priority_display()#② modelJson['State'] =model.get_State_display() tasks.append(modelJson) data={"rows":tasks,'total':len(tasks),'success':True} return JsonResponse( data);
标注①:JobCount移入到Model层,构成model的一个属性函数。标注②:直接返回优先级值的对照信息。
class Task(models.Model): ... def JobCount(self): return self.job_set.count() JobCount.short_description='作业数量'
客户端代码:
<script> getData() function getData() { //模拟异步从后台获得值 $.ajax({ url: "/task/taskGetList/", success: function (result) { //d = JSON.parse(result); d = result for (const task of d.rows) { var row=""; row +="<tr>"; row +="<td>"+task.TaskId+"</td>"; row +="<td>"+task.TaskNum+"</td>"; row +="<td>"+task.Source+"</td>"; row +="<td>"+task.Target+"</td>"; row += "<td>"+task.Barcode+"</td>"; row += "<td>"+task.State+"</td>"; row += "<td>"+task.Priority+"</td>"; row += "<td>"+(task.BeginDate?task.BeginDate:'-')+"</td>"; row += "<td>"+(task.EndDate?task.EndDate:'-')+"</td>"; row += "<td>"+task.SystemDate+"</td>"; row += "<td>"+task.JobCount+"</td>"; row += "<td><a id='" + task.TaskId + "-decompose' href='"+task.TaskId +"/decompose/'>分解</a> <a id='"+task.TaskId +"-start' href='"+task.TaskId +"/start/'>下达</a> <a id='"+task.TaskId +"-change' href='"+task.TaskId +"/change/'>修改</a></td>"; row +="</tr>"; $("#id_task_table tbody").append(row); } }}); } </script>
1.5. 小结
通过本章节内容,我们阐述了如何实现服务端与客户端分离的写法,并仍然采用稳步的渐进式改造方案,采用小步快跑的方式完成本次迭代。最后,我们在功能不变的前提下,完成了技术栈的迁移,把基于模板页渲染的django变成了基于模板框架js异步渲染的主流编程模式,下一章节我们将演示如何如何把编辑页面也进阶到前端渲染。