python工业互联网应用实战17—前后端分离模式之django template vs jquery3
上一章节我们完成了“CRUD”的后面3个功能点,新增由于改动较大我们专门增加本章来阐述,主要是完成技术栈切换后,会发现模板的代码判断过多,逻辑过于复杂。对未来存在的扩展和维护友好性严重下降!
1.1. 数据新增
作为企业开发信息管理的核心“增/删/改/查”,目前为止我们涉及到了查询、修改和删除,现在我们来讲讲如何通过新增添加数据到系统吧。新增操作与修改删除不一样的就是新增的时候对象未在后台持久化到数据库中获取自身的对象标识,也就是说对象标识是空的或“0”,我们也是依据这点来判断数据是新增还是修改。如下图:参考admin新增必填项,其它为默认项。
1.1.1. 修改taskChange.html模板代码
修改现在的taskChange.html模板,支持新增数据录入,本次模板改动较大主要是引入了bootstrap样式,代码如下:
<!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> <div class="container"> <h1>任务详情</h1> <div class="row"><div><b>对象标识:</b></div><div id="task_id">{{pk}}</div></div> {% if pk != '0' %} <!--①--> <div class="row"><div><b>任务号:</b></div><div id="task_num"></div></div> <form method="post" id="edit_form" hidden> <div class="row"> <div><b>源地址:</b></div><input name="source" id="id_source" value="" /> <div><b>目标地址:</b></div><input name="target" id="id_target" value="" /> <input type="button" value="提交" onclick="saveData()"> <input type="button" value="删除" onclick="delData()"> </div> </form> {% else %} <form method="post" id="edit_form"> <div class="row"><div><b>任务号:</b></div><input name="task_num" id="id_task_num" value="" /></div> <div class="row"><div><b>源地址:</b></div><input name="source" id="id_source" value="" /></div> <div class="row"><div><b>目标地址:</b></div><input name="target" id="id_target" value="" /></div> <div class="row"><div><b>条码:</b></div><input name="barcode" id="id_barcode" value="" /><input type="button" value="提交" onclick="saveData()"></div> <div class="row"></div> </form> {% endif %} {% csrf_token %} <div class="row" id="div_source" hidden><div><b>源地址:</b></div><div id="source"></div></div> <div class="row" id="div_target" hidden><div><b>目标地址:</b></div><div id="target"></div></div> <div class="row" id="div_barcode" hidden><div><b>条码:</b></div><div id="barcode"></div></div> <div class="row"><div><b>任务状态:</b></div><div id="state"></div></div> <div class="row"><div><b>优先级:</b></div><div id="priority"></div></div> <div class="row"><div><b>开始时间:</b></div><div id="begin_date"></div></div> <div class="row"><div><b>结束时间:</b></div><div id="end_date"></div></div> <div class="row"><div><b>作业数量:</b></div><div id="job_count"></div></div> </div> <script> if ($('#task_id').text() > 0) { $('#div_barcode').removeAttr('hidden') getData() } else $('#div_barcode').attr('hidden') function getData() { //异步从后台获得值 url_str = "/task/taskGet/" + $('#task_id').text() + '/' $.ajax({ url: url_str, success: function (result) { task = result.model if (task.State == '未处理') { $('#div_source').attr('hidden') $('#div_target').attr('hidden') $('#edit_form').removeAttr('hidden') $('#id_source').val(task.Source) $('#id_target').val(task.Target) } else { $('#edit_form').attr('hidden') $('#div_source').removeAttr('hidden') $('#div_target').removeAttr('hidden') $('#source').text(task.Source) $('#target').text(task.Target) } $('#task_num').text(task.TaskNum) $('#barcode').text(task.Barcode) $('#state').text(task.State) $('#priority').text(task.Priority) $('#begin_date').text(task.BeginDate) $('#end_date').text(task.EndDate) $('#job_count').text(task.JobCount) } }) } function saveData() { //异步提交数据到后台 url_str = "/task/taskSave/" + $('#task_id').text() + '/' data = { source: $('#id_source').val(), target: $('#id_target').val(), csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val() } if ($('#task_id').text() <= 0) //② data.taskNum = $('#id_task_num').val() data.barcode = $('#id_barcode').val() $.ajax({ type: 'POST', url: url_str, data: data, success: function (result) { window.location.replace("/task/"); } }); } function delData() { url_str = "/task/taskDel/" data = { pk: $('#task_id').text(), csrfmiddlewaretoken: $('[name="csrfmiddlewaretoken"]').val() } $.ajax({ type: 'POST', url: url_str, data: data, success: function (result) { window.location.replace("/task/"); } }); } </script> </body> </html>
标注①:增加了对象标识来判断是修加载还是新增,页面html改动不是非常大,主要是增加css规范显示。标注②:如果新增提交参数增加任务编号和条码信息。
1.1.2. 修改后台taskSave代码支持新增model
... def taskSave(request,pk): if int(pk) > 0: #① data={"Source":request.POST['source'],"Target":request.POST['target']} model = Task.objects.filter(pk=pk).update(**data) else: data={"Source":request.POST['source'],"Target":request.POST['target'],"Barcode":request.POST['barcode'],\ "TaskNum":request.POST['taskNum'], "State":1,"Priority":1,} model=Task.objects.create(**data) data={'total':1,'success':True} return JsonResponse(data)
标注①:通过pk值判断新增还是修改操作。
1.1.3. 新增链接
最后我们在tasks.html模板里增加一个新增链接,可以进入到新增窗口。
<!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> <div><a href='0/change/'>新增</a></div> ...
1.1.4. 运行效果
1.2. 重构模板代码/查看
现在的taskChange.html模板代码为了支持查看、修改和新增做了很多隐藏的区域,根据不同的model状态隐藏或显示不同的区域,这个的代码当页面功能复杂的时候,后期维护就会变成一个泥泞的沼泽,会让每个打算过草地的深陷泥潭。重构代码就是复杂的晦涩的代码修改成易读和简洁的代码,尽量让函数、类和模板等功能单一。
1.2.1. 查看模板taskView.html模板代码
查看任务明细信息与修改分开两个模板来处理,把功能内聚到不同的文件里进行处理
<!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> <div class="container"> <h1>任务详情</h1> <div class="row"><div><b>对象标识:</b></div><div id="task_id">{{pk}}</div></div> <div class="row" id="div_source" ><div><b>源地址:</b></div><div id="source"></div></div> <div class="row" id="div_target" ><div><b>目标地址:</b></div><div id="target"></div></div> <div class="row" id="div_barcode" ><div><b>条码:</b></div><div id="barcode"></div></div> <div class="row"><div><b>任务状态:</b></div><div id="state"></div></div> <div class="row"><div><b>优先级:</b></div><div id="priority"></div></div> <div class="row"><div><b>开始时间:</b></div><div id="begin_date"></div></div> <div class="row"><div><b>结束时间:</b></div><div id="end_date"></div></div> <div class="row"><div><b>作业数量:</b></div><div id="job_count"></div></div> </div> <script> if ($('#task_id').text() > 0) { getData() } function getData() { //异步从后台获得值 url_str = "/task/taskGet/" + $('#task_id').text() + '/' $.ajax({ url: url_str, success: function (result) { task = result.model $('#task_num').text(task.TaskNum) $('#source').text(task.Source) $('#target').text(task.Target) $('#barcode').text(task.Barcode) $('#state').text(task.State) $('#priority').text(task.Priority) $('#begin_date').text(task.BeginDate) $('#end_date').text(task.EndDate) $('#job_count').text(task.JobCount) } }) } </script> </body> </html>
模板只专注于查看任务明细的功能,其它新增、修改录入的相关功能全部移除掉。
1.2.2. 发布taskView url
文件:Task/urls.py
from django.urls import path,re_path from Task import views urlpatterns = [ ... re_path('^(?P<pk>\d+)/view/$',views.taskView,name='taskView'),#② ]
文件:Task/views.py
def taskView(request,pk): return render(request,'Task/taskView.html',{"pk":pk})
1.2.3. 修改tasks.html模板代码,增加查看链接
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><a href='"+task.TaskId +"/view/'>"+task.TaskNum +"</a></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>" +(task.State=='未处理'?" <a id='" + task.TaskId + "-change' href='" + task.TaskId + "/change/'>修改</a>" :"" ) + " <a id='" + task.TaskId + "-delete' href='#' onclick=taskDel(" + task.TaskId + ")>删除</a></td>"; row +="</tr>";//② $("#id_task_table tbody").append(row); } } }); }
标注①:增加查看链接;标注②:只能修改“未处理”状态的任务
运行效果
1.3. 重构模板代码/修改
重构代码把新增和修改内聚到taskChange.html模板下,代码聚焦在新增和修改下,不在考虑查看的相关功能处理。
1.3.1. 修改taskChange.html模板代码
<!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> <div class="container"> <h1>任务详情</h1> <div class="row"><div><b>对象标识:</b></div><div id="task_id">{{pk}}</div></div> <form method="post" id="edit_form"> <div class="row"><div><b>任务号:</b></div><input name="task_num" id="id_task_num" value="" /></div> <div class="row"><div><b>源地址:</b></div><input name="source" id="id_source" value="" /></div> <div class="row"><div><b>目标地址:</b></div><input name="target" id="id_target" value="" /></div> <div class="row"><div><b>条码:</b></div><input name="barcode" id="id_barcode" value="" /><input type="button" value="提交" onclick="saveData()"></div> <div class="row"></div> {% csrf_token %} </form> <div class="row"><div><b>任务状态:</b></div><div id="state"></div></div> <div class="row"><div><b>优先级:</b></div><div id="priority"></div></div> <div class="row"><div><b>开始时间:</b></div><div id="begin_date"></div></div> <div class="row"><div><b>结束时间:</b></div><div id="end_date"></div></div> <div class="row"><div><b>作业数量:</b></div><div id="job_count"></div></div> </div> <script> if ($('#task_id').text() > 0) { getData() } function getData() { //异步从后台获得值 url_str = "/task/taskGet/" + $('#task_id').text() + '/' $.ajax({ url: url_str, success: function (result) { task = result.model if (task.State == '未处理') { //② $('#id_task_num').attr('disabled',true) $('#id_barcode').attr('disabled',true) } else { $('#id_task_num').removeAttr('disabled') $('#id_barcode').removeAttr('disabled') } $('#id_source').val(task.Source) $('#id_target').val(task.Target) $('#id_task_num').val(task.TaskNum) $('#id_barcode').val(task.Barcode) $('#state').text(task.State) $('#priority').text(task.Priority) $('#begin_date').text(task.BeginDate) $('#end_date').text(task.EndDate) $('#job_count').text(task.JobCount) } }) } ...
</script> </body> </html>
标注①:删除复杂的if渲染模板;标注②:修改状态把不允许修改的input属性设为只读状态
1.4. 小结
代码重构完成后查看与新增/修改的url和view的处理就分开了成两个分支了,重构让每一部分的代码更专注与相关功能,从而减少条件判断,提高代码的可读性。尽量不要在一个函数里囊括过多的功能、尽量不要在一个类里包括过多的功能、尽量也不要在一个模板里包含太多的功能。让代码高内聚(功能聚焦)低耦合,是高质量代码、提高代码可读性和简洁性的不二法宝。当你的函数臃肿、当你的类功能臃肿、当你的模板代码臃肿,重构代码吧,让它简单读、简洁可读、简明易读!