python工业互联网应用实战15-前后端分离模式1

作者: 博客园精华区  更新时间:2021-05-20 10:22:00  原文链接


我们在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 异步渲染的主流编程模式,下一章节我们将演示如何如何把编辑页面也进阶到前端渲染。