上一章節我們完成了“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的處理就分開了成兩個分支了,重構讓每一部分的程式碼更專注與相關功能,從而減少條件判斷,提高程式碼的可讀性。儘量不要在一個函式裡囊括過多的功能、儘量不要在一個類裡包括過多的功能、儘量也不要在一個模板裡包含太多的功能。讓程式碼高內聚(功能聚焦)低耦合,是高質量程式碼、提高程式碼可讀性和簡潔性的不二法寶。當你的函式臃腫、當你的類功能臃腫、當你的模板程式碼臃腫,重構程式碼吧,讓它簡單讀、簡潔可讀、簡明易讀!