前一章节介绍了List页面的JQuery技术栈的迁移,这一章节我们花一些篇幅来说说修改/查看页面的技术栈迁移。相对于List的获取数据,修改页面涉及到数据Post提交到后台更新数据库。我们仍旧小步迭代的方式推进,修改/查看页面的技术迁移。
1.1. 修改/查看页面分离
修改/查看页面分离与列表的前后台分离会相对复杂一些,主要时是修改页面会涉及到通过POST向后台提交数据修改。基于模板的方式加载数据,技术上是通过view的change函数里增加POST分支来实现数据提交的,提交成功后重定向加载列表url,我们先沿用这一思路采用前后台分离的模式来重构我们的修改/查看页面。
1.1.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> <h1>任务详情</h1> <div id="task_id"></div> <div id="task_num"></div> <form method="post" id="edit_form" hidden> <input name="source" id="id_source" value="" /> <input name="target" id="id_target" value="" /> <input type="submit" value="提交"> {% csrf_token %} </form> <div id="source"></div> <div id="target"></div> <div id="state"></div> <div id="priority"></div> <div id="begin_date"></div> <div id="end_date"></div> <div id="job_count"></div> <script> task = { "TaskId": 2, "TaskNum": 102, "Source": "101", "Target": "05-01-02", "Barcode": "101001001010", "State": "处理成功", "Priority": "紧急", "BeginDate": null, "EndDate": null, "User": 1, "SystemDate": "2021-01-26 05:59:45", "JobCount": 10 } getData() function getData() { #① 数据显示绑定的赋值代码 $('#task_id').text(task.TaskId) $('#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>
标注①:getData函数里,数据显示绑定的赋值代码。
运行效果
1.1.2. 改造任务获取后台
接下来,我们通过增加taskGet函数通过传入对象pk来获取对象的json数据。获取数据的url“ http://localhost:8080/task/taskGet/1/”,获取pk=1的model对象数据。
Task/urls.py文件
from django.urls import path,re_path from Task import views urlpatterns = [ ... path('taskGetList/', views.taskGetList,name='taskGetList') re_path('taskGet/(?P<pk>d+)/$',views.taskGet,name='taskGet'),#① ]
标注①:通过传入pk参数来获取单个model的json格式数据。
Task/urls.py文件
... def taskGet(request,pk): model = get_object_or_404(Task, pk=pk)#① 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() return JsonResponse({"model":modelJson,'total':1,'success':True})
标注①:通过传入pk参数来获取单个model的json格式数据。
运行结果: http://localhost:8080/task/taskGet/1/
1.1.3. 再次重构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> <h1>任务详情</h1> <div id="task_id">{{pk}}</div> <!--①--> <div id="task_num"></div> <form method="post" id="edit_form" hidden> <input name="source" id="id_source" value="" /> <input name="target" id="id_target" value="" /> <input type="submit" value="提交"> {% csrf_token %} </form> <div id="source"></div> <div id="target"></div> <div id="state"></div> <div id="priority"></div> <div id="begin_date"></div> <div id="end_date"></div> <div id="job_count"></div> <script> 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>
标注①:模板需要把请求的pk值,赋值到前端组件,这个很重要所有单个对象均是用pk来获取的。 标注②:前端通过页面组件的pk值,异步获取到任务的json格式数据,并加载显示。
这里我们同样需要改进一下views函数change函数把获取到的pk值,渲染到前端。
@atomic def change(request,pk): if request.method=='GET': return render(request,'Task/taskChange.html',{"pk":pk})#① elif request.method=='POST': data={"Source":request.POST['source'],"Target":request.POST['target']} Task.objects.filter(pk=pk).update(**data) return __reloadTasksPage(request)
标注①:需要把请求的pk值,传到前端模板,原来是直接传入对象到模板。
现在访问地址:http://localhost:8080/task/3/change/结果如下图:
1.2. 提交数据
进一步重构客户端代码,实现可以修改任务为“未处理”状态的源地址和目标地址数据,并提交数据到后台。
<script> getData() function getData() { //异步从后台获得值 url_str = "/task/taskGet/"+ $('#task_id').text() //② $.ajax({ url: url_str , success: function (result) { task = result.model if (task.State == '未处理') { $('#source').attr('hidden') $('#target').attr('hidden') $('#edit_form').removeAttr('hidden') $('#id_source').val(task.Source) $('#id_target').val(task.Target) } else { $('#edit_form').attr('hidden') $('#source').removeAttr('hidden') $('#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) } }); } </script>
运行结果
至此,我们完成了修改页面客户与服务端的代码分离,从而实现了django模板页的解耦。我们构建了基于django的框架下的客户端、服务端主流的开发模式。
1.3. 异步POST数据
目前为止我们提交数据仍然是采用模板方式提交的,如何采用ajax post到后台呢?本小节我们着手处理post的异步提交。
1.3.1. 增加taskSave url
参考前面的views 函数taskGet新增做一个taskSave 并发布url。
from django.urls import path,re_path from Task import views urlpatterns = [ ... re_path('taskGet/(?P<pk>d+)/$',views.taskGet,name='taskGet'), re_path('taskSave/(?P<pk>d+)/$',views.taskSave,name='taskSave'),#① ]
文件:Task/views.py
... def taskSave(request,pk): data={"Source":request.POST['source'],"Target":request.POST['target']} model = Task.objects.filter(pk=pk).update(**data) data={'total':model,'success':True} return JsonResponse(data)
1.3.2. 修改页面代码
... <form method="post" id="edit_form" hidden> <input name="source" id="id_source" value="" /> <input name="target" id="id_target" value="" /> <input type="button" value="提交" onclick="saveData()" > <!--①--> {% csrf_token %} </form>
标注①:修改提交按钮采用JQuery自定义click事件处理数据提交。
<script> ... function saveData() { //异步从后台获得值 url_str = "/task/taskSave/"+ $('#task_id').text() + '/' //① data = { source: $('#id_source').val(), target: $('#id_target').val(), csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val() //② } $.ajax({ type: 'POST',url: url_str, data: data, success: function (result) { //alert('HEHE') window.location.replace("/task/"); //③ } }); } </script>
标注①:数据POST url 例子:/task/taskSave/1/,意思是更新pk=1的model数据。标注②:一定要传回这个参数,否则会报csrf验证失败。标注③:成功修改后从定向url到“/task/”。
1.4. 数据删除
作为企业开发增/删/改/查,目前为止我们涉及到了查询和修改,另外两个重要的就是新增和删除,通过新增添加数据,通过删除操作删除不需要的数据。
1.4.1. 删除操作
我们先来说删除操作,与修改的关键是对象标识(pk),同理,删除也是通过对象标识来进行。我们在查看页面增加删除按钮,来完成删除操作。删除某个任务涉及到后台数据的变化,常规采用POST方式来完成数据提交,这里我们把url设计成:/task/taskDel与保存函数类似的范式,但是不在url中显示要删除的pk值,pk作为post参数传递。
文件:Task/urls.py
from django.urls import path,re_path from Task import views urlpatterns = [ ... path('taskGetList/', views.taskGetList,name='taskGetList'), re_path('taskGet/(?P<pk>d+)/$',views.taskGet,name='taskGet'), re_path('taskSave/(?P<pk>d+)/$',views.taskSave,name='taskSave'), path('taskDel/',views.taskDel,name='taskDel'),#① ]
标注①:不在显示传入pk参数,通过post隐式方式传入对象标识。
文件:Task/views.py
... def taskDel(request): data={'total':0,'success':False} if request.method=='POST': pk=request.POST['pk']#① model = Task.objects.filter(pk=pk).delete() data={'total':model,'success':True} #② return JsonResponse(data)
标注①:获取POST请求里的pk参数。标注②:后台接口返回成功与失败,前端来判断后续的处理。
文件:模板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> <h1>任务详情</h1> <div id="task_id">{{pk}}</div> <div id="task_num"></div> <form method="post" id="edit_form" hidden> <input name="source" id="id_source" value="" /> <input name="target" id="id_target" value="" /> <input type="button" value="提交" onclick="saveData()" > <input type="button" value="删除" onclick="delData()" > <!--①--> {% csrf_token %} </form> <div id="source"></div> <div id="target"></div> <div id="state"></div> <div id="priority"></div> <div id="begin_date"></div> <div id="end_date"></div> <div id="job_count"></div> <script> getData() function getData() { //异步从后台获得值 url_str = "/task/taskGet/" + $('#task_id').text() + '/' $.ajax({ url: url_str, success: function (result) { task = result.model if (task.State == '未处理') { $('#source').attr('hidden') $('#target').attr('hidden') $('#edit_form').removeAttr('hidden') $('#id_source').val(task.Source) $('#id_target').val(task.Target) } else { $('#edit_form').attr('hidden') $('#source').removeAttr('hidden') $('#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() } $.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>
标注①:注意没有传入pk参数。标注②:后台返回成功后,重定向页面到列表页面。
运行程序,在列表页面点击修改进入修改页面,未处理的任务我就可以点击删除按钮删除这条任务记录了,然后重新定向到列表页面就会发现重新刷新的类别没有那条任务记录了,删除操作成功!
1.5. 运行效果
1.6. 小结
通过本章节内容,我们完成了从列表到修改界面的服务端与客户端分离的写法,并仍然采用稳步的渐进式改造方案,采用小步快跑的方式完成本次迭代。最后,我们在功能不变的前提下,完成了技术栈的迁移,把基于模板页渲染的django变成了基于模板框架js异步渲染的主流编程模式。通过例子完整的演示了django如何实现企业开发中遇到“增/删/查/修/”后面3个问题,新增相对较为复杂,我们留给下一章节来重点讲述。