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="//www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title></title>
    <link href="//cdn.bootcss.com/twitter-bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="//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="//www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title>任務列表</title>
    <link href="//cdn.bootcss.com/twitter-bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="//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="//www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title></title>
    <link href="//cdn.bootcss.com/twitter-bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="//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="//www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title></title>
    <link href="//cdn.bootcss.com/twitter-bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="//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的處理就分開了成兩個分支了,重構讓每一部分的代碼更專註與相關功能,從而減少條件判斷,提高代碼的可讀性。盡量不要在一個函數里囊括過多的功能、盡量不要在一個類里包括過多的功能、盡量也不要在一個模板里包含太多的功能。讓代碼高內聚(功能聚焦)低耦合,是高質量代碼、提高代碼可讀性和簡潔性的不二法寶。當你的函數臃腫、當你的類功能臃腫、當你的模板代碼臃腫,重構代碼吧,讓它簡單讀、簡潔可讀、簡明易讀!