赞
踩
目录
django+ajax(juqery)+vue(可选)
不使用websocket长连接
当我们需要在服务器上实现一些复杂逻辑功能时,可能会在一个函数(称为mission)中打印很多内容到python端的控制台,但是这些内容并不是mission的返回值,因此无法直接被view.py中的函数(称为getData)捕获并渲染到页面(称为display)。
关键点在于
console.html中显示一个按钮和文本框,点击按钮后,周期性getData发送get请求,getData中运行mission函数,并动态的将mission中需要打印的内容返回至display.html,并将display.html显示在console.html的文本框中,当mission运行完后,向console.html发送停止指令。
- ---demo //项目容器
- |---demo //主目录,整个项目的配置和调度中心。
- | |---__init__.py //告诉python,该目录是一个包 。
- | |---asgi.py //一个 ASGI 兼容的 Web 服务器的入口,以便运行你的项目。
- | |---settings.py //该 Django 项目的设置/配置。
- | |---urls.py //该 Django 项目的 URL 声明; 一份由 Django 驱动的网站"目录"
- | |---wsgi.py //一个 WSGI 兼容的 Web 服务器的入口,以便运行你的项目
- |---first_app //新建的应用
- | |---migrations //用于数据库迁移,便于数据库管理
- | | |---__init__.py
- | |---templates //用于存放html文件,即网页结构
- | | |---first_app
- | | |---js_lib //存放js库文件
- | | |---console_vue.js //自己写的控制代码
- | | |---jquery.min.js //jquery,包装了ajax
- | | |---vue.js //vue库
- | | |---console.html //主页,控制台
- | | |---display.html //显示后端打印的信息
- | |---__init__.py
- | |---admin.py //自带的后台管理
- | |---apps.py //创建对应app类的文件
- | |---models.py //mtv中的m,用于和数据库交互
- | |---tests.py //用于开发测试用例
- | |---urls.py //first_app的urls目录
- | |---views.py //mtv中的v,用于处理网页的后端业务逻辑
- |---third_project //功能代码
- | |--mission.py //具体功能的实现
- | |--my_thread.py //线程类及控制函数
- |---db.sqlite3 //轻量级数据库,用于和models交互
- |---manage.py //一个实用的命令行工具,可让你以各种方式与该 Django 项目进行交互。
console.html
使用v-html="message"接收数据。
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8" />
- <title>动态显示</title>
- <style type="text/css">
- #result {
- width: 100%;
- height: 600px;
- border: solid 1px #90b;
- }
- </style>
- </head>
- <body>
- <div id="contain">
- <button id="btn" @click="funA">funA</button>
- <div id="result">
- <p v-html="message"></p>
- </div>
- </div>
-
- <script src="/static/first_app/js_lib/jquery.min.js"></script>
- <script src="/static/first_app/js_lib/vue.js"></script>
-
- <script src="/static/first_app/js_lib/console_vue.js"></script>
- </body>
- </html>
console_vue.js
这里使用id = setInterval(function_handle, millisecond)实现周期性的发送get请求,并且使用clearInterval(id)停止周期性的发送请求。
这里需要注意的是function_handle不能包含参数,但是我们可以通过嵌套的方式返回一个不带参数的函数句柄。
- var app = new Vue({
- el: "#contain",
- data: {
- message: "",
- },
- methods: {
- funA: function () {
- var that = this;
- var timer = window.setInterval(_send(that), 4 * 1000); // 不能调用带参数的函数句柄,可以通过嵌套的方式返回一个不带参数的函数句柄
- function _send(_param){
- return function() { // 返回不带参数的函数句柄
- send(_param);
- }
- };
- function send(obj){
- $.get("/display/", function (data) {
- console.log(data)
- if(data == "stop") {
- window.clearInterval(timer); // 使用setInterval返回的id作为参数
- console.log("timer is stoped")
- }
- else
- obj.message = data;
- });
- }
- },
- },
- });
view.py
两个全局变量result用来存放mission打印的信息和thread_dict用来存放线程的状态。
这里直接用getData调用thread_controller接口即可,接口的参数会在后面解释。
- from django.shortcuts import render
- from third_project.my_thread import thread_controller
- from third_project.mission import print_message
-
- # Create your views here.
- result = {}
- thread_dict = {}
-
- def home(request):
- '''首页'''
- return render(request, 'first_app/console.html')
-
- def getData(request):
- '''getData'''
- return thread_controller(request, result, thread_dict,
- "getData", print_message, [1, 10],
- 'first_app/display.html')
mission.py
间隔一秒向列表中添加递增的整数,并将列表存放在字典中。
- import time
-
- def print_message(start, end, dict):
- dict['print_message'] = []
- for i in range(end - start):
- dict['print_message'].append(i + start)
- time.sleep(1)
e.g.
print_message(1,5,{})
{'print_message':[1,2,3,4]}
my_thread.py
# result = {} 存放需要打印的结果,key为被调用的函数名,value为list
# thread_dict = {}
- # 存放线程状态,key为view中开启线程的函数名,value为state
- # state 意义
- # stop 停止或未创建
- # start 开始运行
- # pause 暂停运行
- # end 被调用的函数执行完毕了
- # killed 线程已杀死
- # request http响应内容
- # result 需要打印的结果
- # thread_dict 线程状态
- # func_name view.py中函数的名字
- # callback 被调用的函数名
- # args callback执行需要的参数
- # html_path 返回的url
- from django.http import HttpResponse
- from django.shortcuts import render
- import threading
-
- def thread_controller(request, result, thread_dict, func_name, callback, args, html_path):
-
- if result.get(func_name, "NA") == "NA":
- result[func_name] = {}
- thread_dict[func_name] = "stop"
- res = result[func_name]
- state = thread_dict[func_name]
-
- if state == "stop":
- test_thread = MyThread(thread_dict, func_name, callback, args, res)
- test_thread.start()
- test_thread.resume()
- elif state == "end":
- thread_dict[func_name] = "killed"
- return render(request, html_path, res)
- elif state == "killed":
- result[func_name] = {}
- thread_dict[func_name] = "stop"
- return HttpResponse("stop")
- return render(request, html_path, res)
-
-
- class MyThread(threading.Thread):
- def __init__(self, state_dict, thread_name, func_handle, p_list, g_dict):
- super(MyThread, self).__init__()
- self.g_dict = g_dict
- self.func_handle = func_handle
- self.p_list = p_list
- self.p_list.append(g_dict)
-
- self.paused = True # Start out paused.
- self.isend = False # thread is end.
- self.thread_name = thread_name
- self.state_dict = state_dict
- self.cond = threading.Condition()
-
- def run(self):
- while True:
- with self.cond: # 在该条件下操作
- self.resume()
- list(map(self.func_handle, *zip(self.p_list))) # 调用外部函数
- self.end()
- if self.paused:
- self.cond.wait() # Block execution until notified.
- if self.isend:
- break
-
- def resume(self): # 用来恢复/启动run
- with self.cond: # 在该条件下操作
- self.paused = False
- self.state_dict[self.thread_name] = "start"
- self.cond.notify() # Unblock self if waiting.
-
- def pause(self): # 用来暂停run
- with self.cond: # 在该条件下操作
- self.paused = True # Block self.
- self.state_dict[self.thread_name] = "paused"
-
- def end(self): # 用来结束run
- with self.cond: # 在该条件下操作
- self.isend = True # Block self.
- self.state_dict[self.thread_name] = "end"
-
- def kill(self): # 用来杀死线程
- with self.cond: # 在该条件下操作
- self._delete()
- self.state_dict[self.thread_name] = "killed"
注意这里有一个关键点,我们在写线程类的时候,没法知道需要调用什么函数,以及需要什么参数,因此这里使用了map函数,实现将函数句柄和作为参数,便于封装和使用。
另外要注意Python2和3中map的使用方法有些区别
- #python2直接返回列表
- map(func_handle, [1,2,3])
- def square(x) : # 计算平方数
- return x ** 2
-
- # 计算列表各个元素的平方
- map(square, [1,2,3,4,5])
- # [1, 4, 9, 16, 25]
-
- # 使用 lambda 匿名函数
- map(lambda x: x ** 2, [1, 2, 3, 4, 5])
- # [1, 4, 9, 16, 25]
-
- # 多参数函数
- map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])
- # [3, 7, 11, 15, 19]
-
-
-
- #python3返回的是迭代器
- def square(x) : # 计算平方数
- return x ** 2
-
- # 计算列表各个元素的平方,返回迭代器
- map(square, [1,2,3,4,5])
- # <map object at 0x100d3d550>
-
- # 使用 list() 转换为列表
- list(map(square, [1,2,3,4,5]))
- # [1, 4, 9, 16, 25]
-
- # 使用 lambda 匿名函数
- list(map(lambda x: x ** 2, [1, 2, 3, 4, 5]))
- # [1, 4, 9, 16, 25]
-
-
- # 多参数函数,使用zip函数进行捆绑
- def add(x, y) : # 计算和
- return x + y
-
- list(map(add, *zip([1, 3, 5, 7, 9])))
- # [4, 8, 12, 16]
左边的文本框显示了打印的结果
右边的waterfall可以看出请求时周期进行的
底部的stop和timer is stoped说明前端的周期性请求已经停止
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。