本章节我们将实现与admin里类似的列操作“下达”功能,演示客户端是如何实现操作功能,同时,演示也会强调一点,何时合并你的功能代码,避免相同功能使用不同的代码段来实现,在企业开发中非常重要,良好的编程习惯会让你在未来的维护和扩展中体会到什么叫“好的代码”。

1.1. Table增加操作列

  本例中我们采用url http://localhost:8001/task/1/start/ 来相应对某行任务执行“下达”操作,类似RESTful的接口模式后面的动词代码某个操作,现在在table中增加一列操作,每行显示下达操作链接,代码如下:

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<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>
{% for task in tasks %} <tr>
<td>{{task.TaskId }}</td>
<td>{{task.TaskNum}}</td>
<td>{{task.Source}}</td>
<td>{{task.Target}}</td>
<td>{{task.Barcode}}</td>
<td>{{task.get_State_display}}</td>
<td>{{task.get_Priority_display}}</td>
<td>-</td>
<td>-</td>
<td>{{task.job_set.count}}</td>
<td><a href="{{task.TaskId }}/start/">下达</a></td>
</tr>
{%endfor%}
</table> </body>
</html>

  运行效果:

1.2. Task APP增加相应url和views函数

  接下来在Task urls.py文件里增加“/1/start/”发布下达的url,代码如下:

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'),#① ]

  标注①:正则表达式,来实现Task_id/start/,针对某个对象标识id执行下达命令。

  接下来在Task/views.py文件里添加start函数代码。

from django.shortcuts import get_object_or_404
from django.shortcuts import redirect
from django.db.transaction import atomic from .TaskBiz import TaskBiz,Task @atomic
def start(request,pk): obj = get_object_or_404(Task, pk=pk)#①
biz= TaskBiz()
biz.task_start(obj)#② #重新刷新列表界面
co_path = request.path.split('/')
new_path=co_path[0:2]
new_path='/'.join(new_path)
request.path = new_path
return redirect(new_path)

  标注①:通过主键获取到任务对象。

  标注②:对获取的任务对象,执行业务逻辑层的task_start函数,这里的业务逻辑直接沿用admin重构组织的那个TaskBiz.py业务逻辑类里的任务“下达”函数。

  章节到这里我希望读者能够体会到代码重用的好处,把业务抽象出一个单独的层,比放在admin里是不是有优势多了,不需要在客户端的“下达”时重新再实现一遍这个功能。

  点击操作列里的下达链接,任务就会从“处理成功”改成“下达”状态。

1.3. 修改操作和详情页面

  依据“下达”方式,url:http://localhost:8001/Task/1/change/就是跳到修改/详情界面查看和修改该任务详情数据。同样,我们还是采用渐进的原则的来推进这个功能的实现。

  首先,增加Task/urls.py 增加change url。

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'),#② ]

  标注②:change函数与start函数类似的写法,通过传入pk来获取需要修改的对象。

  然后,我们修改Task/views.py文件内容,增加change函数。

...

def change(request,pk):

    obj = get_object_or_404(Task, pk=pk)
return render(request,'Task/taskChange.html',{"task":obj})

  其次,我们采用模板页把task对象数据渲染到html上,这里我采用先加载显示出来。

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<div>{{task.TaskId }}</div>
<div>{{task.TaskNum}}</div>
<div>{{task.Source}}</div>
<div>{{task.Target}}</div>
<div>{{task.Barcode}}</div>
<div>{{task.get_State_display}}</div>
<div>{{task.get_Priority_display}}</div>
<div>{{task.BeginDate}}</div>
<div>{{task.EndDate}}</div>
<div>{{task.job_set.count}}</div>
</body>
</html>

  运行结果

  最后,我们把模板页面修改成html input输入框,实现可以向后台post数据。本例我们假定未处理状态的任务可以修改源地址和目标地址信息。

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<h1>任务详情</h1>
<div>{{task.TaskId }}</div>
<div>{{task.TaskNum}}</div>
{% if task.State == 1%} <!--①-->
<form method="post">
<input name="source" id="id_source" value="{{task.Source}}" />
<input name="target" id="id_target" value="{{task.Target}}" />
<input type="submit" value="提交">
</form>
{% else %}
<div>{{task.Source}}</div>
<div>{{task.Target}}</div>
{% endif %} <div>{{task.get_State_display}}</div>
<div>{{task.get_Priority_display}}</div>
<div>{{task.BeginDate}}</div>
<div>{{task.EndDate}}</div>
<div>{{task.job_set.count}}</div>
</body>
</html>

  标注①:模板增加了 {% if %}判断,状态等于1 待处理状态的我们才能修改任务的源和目标地址,已经处理完成的任务就只能查看详情了,否则,job数据与task的数据逻辑就不一致了。

  企业开发过程中,确保数据逻辑的前后一致性是非常关键和重要的。

  运行效果:

1.4. 把post数据更新到数据库

  现在修改数据后尝试点击提交按钮,通常情况下你会得到下面得错误提示页面,CSRF错误提示页面。

  Django针对CSRF得保护措施是在生成得每个表单中放置一个自动生成的令牌,通过这个令牌判断POST请求是否来自同一个网站。我们在<form></form>内放置一个{% csrf_token %} 即可,更多CSRF内容参考官网文档。

  运行结果

  现在报后台错误了,接下来我们实现views.py change函数。

@atomic
def change(request,pk):
if request.method=='GET': #①
obj = get_object_or_404(Task, pk=pk)
return render(request,'Task/taskChange.html',{"task":obj})
elif request.method=='POST': #②
data={"Source":request.POST['source'],"Target":request.POST['target']}
Task.objects.filter(pk=model.pk).update(**data) #重新刷新列表界面 #③
co_path = request.path.split('/')
new_path=co_path[0:2]
new_path='/'.join(new_path)
request.path = new_path
return redirect(new_path)

  ①:change函数get请求情况下返回查看详情页面;

  ②:post请求情况下,使用post过来的参数更新对象属性;

  ③: 数据更新完成后重定向到列表页(也会重新加载列表数据,从而显示更新后的值);

  列表及时反映对象属性的变更是企业开发中常见的操作方式,否则用户就不知道这次修改和调整状态是否完成,不断的来回点击修改。

  这里本人也讲述一下使用VS 2019常用到的一种调试方式就是在代码上打上断点,debug模式运行当程序执行到断点时会中断当前执行,便于开发人员验证过程的变量是否符合预期。

  好的,现在就在change函数内部打上断点,debug运行我们的工程,点击提交按钮在IDE里调试我们的代码,修正错误的写法。

  数据修改成功!

1.5. 代码重复

  上面的代码中,笔者通过copy的方式把重定向到列表界面代码段在分别在start和change函数中重复了。遇到这种情况大多数开发人员尤其新手都会忽略,对于重复代码我们到底该怎么办?“事不过三”如果重复三次了一定得封装到一个函数里,这里我们直接把这段代码封装成__reloadTasksPage函数。

...
#重定向到列表界面 #③
co_path = request.path.split('/')
new_path=co_path[0:2]
new_path='/'.join(new_path)
request.path = new_path
return redirect(new_path)

  重构后的代码

...

@atomic
def start(request,pk):
#pk=request.GET.get('pk') obj = get_object_or_404(Task, pk=pk)#① biz= TaskBiz()
biz.task_start(obj) #② return __reloadTasksPage(request) @atomic
def change(request,pk):
if request.method=='GET': #①
obj = get_object_or_404(Task, pk=pk)
return render(request,'Task/taskChange.html',{"task":obj})
elif request.method=='POST': #②
data={"Source":request.POST['source'],"Target":request.POST['target']}
Task.objects.filter(pk=pk).update(**data) return __reloadTasksPage(request) def __reloadTasksPage(request):
#重新刷新列表界面 #③
co_path = request.path.split('/')
new_path=co_path[0:2]
new_path='/'.join(new_path)
request.path = new_path
return redirect(new_path)

  代码是不是简洁了好多,可能这个段代码重构会多花我们一点时间,长远来看这点时间事非常值得的,尤其后面如果调整到reloadTasksPage函数里的具体实现,大量散落和重复的代码是后期维护和扩展的噩梦!“敏捷”模式不提倡过度设计,但是如果“重复三次”,请重构你的代码。

1.6. 小结

  本章我们详细的说明了如何实现客户端操作,读者可以自己试一试增加“处理”操作,实现对未处理状态的任务进行作业分解。客户端的操作会存在两种一种就是直接改变任务的状态,另外一种就是类似查看详情操作,这个种操作我们需要通过模板把数据加载处理,任务分解和下达之类的操作,更新完数据后重新加载数据即可。django对于这两种模式可以都是使用url和view组合来完成,这样在技术上两种模式就不存区别了,大大提高了开发效率。

PYTHON工业互联网应用实战12—客户端操作的更多相关文章

  1. python工业互联网应用实战11—客户端UI

    这个章节我们将演示用户端界面的开发,当前演示界面还是采用先实现基本功能再逐步完善的"敏捷"模式.首先聚焦在功能逻辑方面实现普通用户与系统的交互,普通用户通过url能查看到当前任务的 ...

  2. python工业互联网应用实战2—从需求开始

    前言:随着国家工业2025战略的推进,工业互联网发展将会提速,将迎来一个新的发展时期,越来越多的企业开始逐步的把产线自动化,去年年底投产的小米亦庄的智能工厂就是一个热议的新闻.小米/华为智能工厂只能说 ...

  3. python工业互联网应用实战5—Django Admin 编辑界面和操作

    1.1. 编辑界面 默认任务的编辑界面,对于model属性包含"choices"会自动显示下来列表供选择,"datetime"数据类型也默认提供时间选择组件,如 ...

  4. python工业互联网应用实战3—模型层构建

    本章开始我们正式进入到实战项目开发过程,如何从需求分析获得的实体数据转到模型设计中来,变成Django项目中得模型层.当然,第一步还是在VS2019 IDE环境重创建一个工程项目,本文我们把工程名称命 ...

  5. python工业互联网应用实战1—SQL与ORM

    从sql到ORM应该说也是编程体系逐步演化的结果,通过类和对象更好的组织开个过程中遇到的各种业务问题,面向对象的解耦和内聚作为一套有效的方法论,对于复杂的企业应用而言确实能够解决实践过程中很多问题. ...

  6. python工业互联网应用实战13—基于selenium的功能测试

    本章节我们再来说说测试,单元测试和功能测试.单元测试我们在数据验证章节简单提过了,本章我们进一步如何用单元测试来测试view的功能代码:同时,也涉及一下基于selenium的功能测试做法.笔者过去的项 ...

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

    我们在13章节里通过监控界面讲了如何使用jquery的动态加载数据写法,通过简单案例来说明了如何实现动态的刷新监控界面的数据,本章我们将演示如何从Django模板加载数据逐步演化到前后端分离的异步数据 ...

  8. python工业互联网应用实战7—业务层

    本章我们演示代码是如何"进化"的,实战的企业日常开发过程中,系统功能总伴随着业务的不断增加,早期简单的代码慢慢的越来越复杂,敏捷编程中的"禅"--简单设计.快速 ...

  9. python工业互联网应用实战14——单元测试覆盖率

    前面的章节我们完成了任务管理主要功能的开发及单元测试编写,可如何知道单元测试效果怎么样呢?测试充分吗?还有没有没有测到的地方呢? 本章节我们介绍一个统计测试代码覆盖率的利器Coverage,Cover ...

随机推荐

  1. 美最大政媒《国会山报》罕见发文阐述BTC,华盛顿金融盛赞SPC

    比特币价格突破4万美元创下历史新高,美国最大政治媒体之一<国会山报>罕见的发表了文章对比特币进行阐明. 2021年已经过去一周,但比特币依然没有停下上涨的步伐.在刚刚过去2020年里,比特 ...

  2. 3. Vue语法--计算属性

    一. 计算属性 1. 什么是计算属性? 通常, 我们是在模板中, 通过插值语法显示data的内容, 但有时候我们可能需要在{{}}里添加一些计算, 然后在展示出来数据. 这时我们可以使用到计算属性 先 ...

  3. 还原Oracle数据库dmp文件(Win系统)

    准备工作: 1.核对数据字符集:   一般Oracle在安装的时候默认是选择ZHS16GBK,如有改动,使用 select userenv('language') from dual;语句查看使用的字 ...

  4. PID算法验证

    算法: struct PID { float kp; float kpnfac; float ki; float kinfac; float kd; }; float gCurPPM = 1300; ...

  5. 后端程序员之路 8、一种内存kv数据库的实现

    键值(Key-Value)存储数据库,这是一种NoSQL(非关系型数据库)模型,其数据按照键值对的形式进行组织.索引和存储.KV存储非常适合不涉及过多数据关系业务关系的业务数据,同时能有效减少读写磁盘 ...

  6. 剑指 Offer 68 - I. 二叉搜索树的最近公共祖先 + 二叉排序树 + 最近公共祖先

    剑指 Offer 68 - I. 二叉搜索树的最近公共祖先 Offer_68_1 题目描述 方法一:迭代法 由于该题的二叉树属于排序二叉树,所以相对较简单. 只需要判断两个结点是否在根节点的左右子树中 ...

  7. PAT-1150(Travelling Salesman Problem)旅行商问题简化+模拟图+简单回路判断

    Travelling Salesman Problem PAT-1150 #include<iostream> #include<cstring> #include<st ...

  8. PTA1071 - Speech Patterns - map计算不同单词个数

    题意 输出给定字符串出现最多的字符串(小写输出)和出现次数. 所求字符串要求:字符中可以含有A-Z.0-9. 比如说题目给出的Can1,我们可以转换成can1,can1就算一个字符串整体,而不是单独的 ...

  9. java将数据生成csv文件

    1,httpRequest接口触发进程[或者可以换成其他方式触发] /** * 出入库生成CSV文件 * @param req * @param params * @return */@Request ...

  10. 快速了解C# 8.0中“可空引用类型(Nullable reference type)”语言特性

    Visual C# 8.0中引入了可空引用类型(Nullable reference type),通过编译器提供的强大功能,帮助开发人员尽可能地规避由空引用带来的代码问题.这里我大致介绍一下可空引用类 ...