我們在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異步渲染的主流編程模式,下一章節我們將演示如何如何把編輯頁面也進階到前端渲染。
