""" 补充 我们用pycharm创建的django项目会自动帮你创建templates文件夹并且是全局的 其实除了可以在全局创建模版文件夹之外,还可以做到更加的细化 就是在每一个应用下创templates模版文件夹 如果出现多个应用和全局都有模版文件夹的情况,那么会优先查找全局 如果全局没有,则按照配置文件中注册app的顺序的从上往下一次查找每一个应用下templates,直到寻找对应名的html INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01.apps.App01Config', ] 每一个应用都可以有自己的urls.py(路由分发),views.py,templates,static """
配置完成后同时支持http和websocket的原因
class ProtocolTypeRouter: """ Takes a mapping of protocol type names to other Application instances, and dispatches to the right one based on protocol name (or raises an error) """ def __init__(self, application_mapping): self.application_mapping = application_mapping if "http" not in self.application_mapping: self.application_mapping["http"] = AsgiHandler
前期三步配置完成后继续书写以下代码
# s13_day02/s13_day02/routing.py from channels.routing import ProtocolTypeRouter,URLRouter from django.conf.urls import url from app01 import consumers application = ProtocolTypeRouter({ 'websocket':URLRouter([ # websocket相关的路由 url(r'^chat/',consumers.ChatConsumer) ]) })
# s13_day02/app01/consumers.py from channels.generic.websocket import WebsocketConsumer class ChatConsumer(WebsocketConsumer): def websocket_connect(self, message): """客户端请求建立链接时 自动触发""" pass def websocket_receive(self, message): """客户端发送数据过来 自动触发""" pass def websocket_disconnect(self, message): """客户端断开链接之后 自动触发""" pass # s13_day02/app01/templates/index.html ... <script> var ws = new WebSocket('ws://127.0.0.1:8000/chat/') </script> # s13_day02/s13_day02/urls.py from django.contrib import admin from django.urls import path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), # 所有的http请求 path('index/', views.index), ] # s13_day02/app01/views.py from django.shortcuts import render def index(request): return render(request,'index.html')
""" http协议 index路径 index视图函数 访问:浏览器窗口直接输入的地址的 (浏览器窗口只支持http,https协议) websocket协议 chat路径 ChatConsumer视图类 访问:new WebSocket对象访问 (如上示例,通过前端创建 WebSocket对象) """
方法总结
# 后端 3个 class ChatConsumer(WebsocketConsumer): def websocket_connect(self, message): """客户端请求建立链接时 自动触发""" self.accept() # 建立链接 并且自动帮你维护每一个客户端 def websocket_receive(self, message): """客户端发送数据过来 自动触发""" # print(message) # message = {'type': 'websocket.receive', 'text': 'hello world!'} text = message.get('text') # 真正的数据 # 给客户端发送消息 self.send(text_data='介绍女朋友') def websocket_disconnect(self, message): """客户端断开链接之后 自动触发""" raise StopConsumer() # 前端 4个 var ws = new WebSocket('ws://127.0.0.1:8000/chat/'); // 1 握手环节验证成功之后 自动触发 onopen ws.onopen = function () { console.log('握手成功') } // 2 给服务端发送消息 send function sendMsg() { ws.send($('#txt').val()) } // 3 一旦服务端有消息 自动触发 onmessage ws.onmessage = function (args) { // console.log(args) // args是一个对象 // 获取发送的数据 console.log(args.data) } // 4 断开链接之后 自动触发 onclose ws.onclose = function () { ws.close() }
我们是通过自己维护一个列表存储链接对象的方式完成了简易版本的群聊
其实channels给你提供了一个用于做群聊的模块,该模块可以实现真正的分组聊天
代码示例:
# s13_day02/app01/consumers.py from channels.generic.websocket import WebsocketConsumer from channels.exceptions import StopConsumer consumer_object_list = [] class ChatConsumer(WebsocketConsumer): def websocket_connect(self, message): """客户端请求建立链接时 自动触发""" # print('请求链接') self.accept() # 建立链接 并且自动帮你维护每一个客户端 # 将链接在列表中存储一份 consumer_object_list.append(self) def websocket_receive(self, message): """客户端发送数据过来 自动触发""" # print(message) # message = {'type': 'websocket.receive', 'text': 'hello world!'} text = message.get('text') # 真正的数据 # 给客户端发送消息 单独发送 # self.send(text_data=text) # 给所有的链接对象发送数据 for obj in consumer_object_list: obj.send(text_data=text) def websocket_disconnect(self, message): """客户端断开链接之后 自动触发""" # print('断开链接') # 客户端断开链接之后 应该将当前对象移除 consumer_object_list.remove(self) raise StopConsumer() # s13_day02/app01/templates/index.html <body> <h1>聊天室</h1> <input type="text" id="txt"> <button onclick="sendMsg()">发送</button> <h1>聊天记录</h1> <div class="record"></div> <script> var ws = new WebSocket('ws://127.0.0.1:8000/chat/'); // 1 握手环节验证成功之后 自动触发 onopen ws.onopen = function () { console.log('握手成功') } // 2 给服务端发送消息 send function sendMsg() { ws.send($('#txt').val()) } // 3 一旦服务端有消息 自动触发 onmessage ws.onmessage = function (args) { // console.log(args) // args是一个对象 // 获取发送的数据 {#console.log(args.data)#} {#alert(args.data)#} // 1 创建p标签 var pEle = $('<p>'); pEle.text(args.data); $('.record').append(pEle) } // 4 断开链接之后 自动触发 onclose ws.onclose = function () { ws.close() } </script> </body> # s13_day02/s13_day02/routing.py from channels.routing import ProtocolTypeRouter,URLRouter from django.conf.urls import url from app01 import consumers application = ProtocolTypeRouter({ 'websocket':URLRouter([ # websocket相关的路由 url(r'^chat/',consumers.ChatConsumer) ]) })
是一个前端插件,跟go和js没有半毛钱关系
主要可以通过代码动态的生成和修改图表数据(组织架构图,执行流程图等等)
网址:https://gojs.net/latest/index.html
如果你想使用,需要下载他的文件
目前需要我们了解的文件其实只有三个,用得到的只有两个
""" 1.go.js 是使用gojs所必须要导入的js文件 2.go-debug.js 可以帮你打印一些bug日志 正常线上不会使用 3.Figures.js go.js中自带了一些基本的图标,额外扩展的图标需要引入该文件 """ # 总结你在使用的导入go.js和Figures.js就够了 (直接把这两文件放入项目根路径下,把下方代码复制进body体内即可)
gojs使用基本套路是先在页面上写一个div站地方,之后初始化该div,再然后所有的图标渲染都在该div内进行!!!
<div id="myDiagramDiv" style="500px; height:350px; background-color: #DAE4E4;"></div> <script src="go.js"></script> <script> var $ = go.GraphObject.make; // 第一步:创建图表 var myDiagram = $(go.Diagram, "myDiagramDiv"); // 创建图表,用于在页面上画图 // 第二步:创建一个节点,内容为jason var node = $(go.Node, $(go.TextBlock, {text: "jason"})); // 第三步:将节点添加到图表中 myDiagram.add(node) </script>
-
TextBlock 创建文本
-
Shape 创建图形
-
Node 节点(结合文本与图形)
-
Links 箭头
TextBlock
<div id="myDiagramDiv" style="500px; height:350px; background-color: #DAE4E4;"></div> <script src="go.js"></script> <script> var $ = go.GraphObject.make; // 第一步:创建图表 var myDiagram = $(go.Diagram, "myDiagramDiv"); // 创建图表,用于在页面上画图 var node1 = $(go.Node, $(go.TextBlock, {text: "jason"})); myDiagram.add(node1); var node2 = $(go.Node, $(go.TextBlock, {text: "jason", stroke: 'red'})); // 字体色 myDiagram.add(node2); var node3 = $(go.Node, $(go.TextBlock, {text: "jason", background: 'lightblue'})); // 背景色 myDiagram.add(node3); </script>
<div id="myDiagramDiv" style="500px; height:350px; background-color: #DAE4E4;"></div> <script src="go.js"></script> <script src="Figures.js"></script> <script> var $ = go.GraphObject.make; // 第一步:创建图表 var myDiagram = $(go.Diagram, "myDiagramDiv"); // 创建图表,用于在页面上画图 var node1 = $(go.Node, $(go.Shape, {figure: "Ellipse", 40, height: 40}) ); myDiagram.add(node1); var node2 = $(go.Node, $(go.Shape, {figure: "RoundedRectangle", 40, height: 40, fill: 'green',stroke:'red'}) ); myDiagram.add(node2); var node3 = $(go.Node, $(go.Shape, {figure: "Rectangle", 40, height: 40, fill: null}) ); myDiagram.add(node3); var node4 = $(go.Node, $(go.Shape, {figure: "Club", 40, height: 40, fill: 'red'}) ); myDiagram.add(node4); </script>
<div id="myDiagramDiv" style="500px; height:350px; background-color: #DAE4E4;"></div> <script src="go.js"></script> <script src="Figures.js"></script> <script> var $ = go.GraphObject.make; // 第一步:创建图表 var myDiagram = $(go.Diagram, "myDiagramDiv"); // 创建图表,用于在页面上画图 var node1 = $(go.Node, "Vertical", // 垂直方向 (字在图形正下方) { background: 'yellow', padding: 8 }, $(go.Shape, {figure: "Ellipse", 40, height: 40,fill:null}), $(go.TextBlock, {text: "jason"}) ); myDiagram.add(node1); var node2 = $(go.Node, "Horizontal", // 水平方向 (字在图形水平右侧) { background: 'white', padding: 5 }, $(go.Shape, {figure: "RoundedRectangle", 40, height: 40}), $(go.TextBlock, {text: "jason"}) ); myDiagram.add(node2); var node3 = $(go.Node, "Auto", // 居中 (字在图形正中间) $(go.Shape, {figure: "Ellipse", 80, height: 80, background: 'green', fill: 'red'}), $(go.TextBlock, {text: "jason"}) ); myDiagram.add(node3); </script>
<div id="myDiagramDiv" style="500px; min-height:450px; background-color: #DAE4E4;"></div> <script src="go.js"></script> <script> var $ = go.GraphObject.make; var myDiagram = $(go.Diagram, "myDiagramDiv", {layout: $(go.TreeLayout, {angle: 0})} ); // 创建图表,用于在页面上画图 var startNode = $(go.Node, "Auto", $(go.Shape, {figure: "Ellipse", 40, height: 40, fill: '#79C900', stroke: '#79C900'}), $(go.TextBlock, {text: '开始', stroke: 'white'}) ); myDiagram.add(startNode); var downloadNode = $(go.Node, "Auto", $(go.Shape, {figure: "RoundedRectangle", height: 40, fill: 'red', stroke: '#79C900'}), $(go.TextBlock, {text: '下载代码', stroke: 'white'}) ); myDiagram.add(downloadNode); var startToDownloadLink = $(go.Link, {fromNode: startNode, toNode: downloadNode}, $(go.Shape, {strokeWidth: 1}), $(go.Shape, {toArrow: "OpenTriangle", fill: null, strokeWidth: 1}) ); myDiagram.add(startToDownloadLink); </script>
1.如何做到数据的前后端交互,图标的动态修改
2.如何去除gojs自带的水印
数据绑定式
<div id="diagramDiv" style="100%; min-height:450px; background-color: #DAE4E4;"></div> <script src="go.js"></script> <script> var $ = go.GraphObject.make; var diagram = $(go.Diagram, "diagramDiv",{ layout: $(go.TreeLayout, { angle: 0, nodeSpacing: 20, layerSpacing: 70 }) }); // 创建一个节点模版 diagram.nodeTemplate = $(go.Node, "Auto", $(go.Shape, { figure: "RoundedRectangle", fill: 'yellow', stroke: 'yellow' }, new go.Binding("figure", "figure"), new go.Binding("fill", "color"), new go.Binding("stroke", "color")), $(go.TextBlock, {margin: 8}, new go.Binding("text", "text")) ); // 创建一个箭头模版 diagram.linkTemplate = $(go.Link, {routing: go.Link.Orthogonal}, $(go.Shape, {stroke: 'yellow'}, new go.Binding('stroke', 'link_color')), $(go.Shape, {toArrow: "OpenTriangle", stroke: 'yellow'}, new go.Binding('stroke', 'link_color')) ); // 这里的数据后期就可以通过后端来获取 var nodeDataArray = [ {key: "start", text: '开始', figure: 'Ellipse', color: "lightgreen"}, {key: "download", parent: 'start', text: '下载代码', color: "lightgreen", link_text: '执行中...'}, {key: "compile", parent: 'download', text: '本地编译', color: "lightgreen"}, {key: "zip", parent: 'compile', text: '打包', color: "red", link_color: 'red'}, {key: "c1", text: '服务器1', parent: "zip"}, {key: "c11", text: '服务重启', parent: "c1"}, {key: "c2", text: '服务器2', parent: "zip"}, {key: "c21", text: '服务重启', parent: "c2"}, {key: "c3", text: '服务器3', parent: "zip"}, {key: "c31", text: '服务重启', parent: "c3"} ]; diagram.model = new go.TreeModel(nodeDataArray); // 动态控制节点颜色变化 var node = diagram.model.findNodeDataForKey("zip"); diagram.model.setDataProperty(node, "color", "lightgreen"); </script>
如何去除gojs自带的水印
需要修改go.js文件源码
# 7eba17a4ca3b1a8346 a.kr=b.V[Ra("7eba17a4ca3b1a8346")][Ra("78a118b7")](b.V,Jk,4,4);
- 在后面添加一行新的代码
a.kr=function(){return false};
通过ssh远程链接服务器并执行响应的操作,类似于XShell
ps:ansible批量管理服务器工具,底层用的就是paramiko模块
安装
pip3 install paramiko
远程链接服务器的方式
-
用户名和密码
-
公钥私钥的方式
paramiko上面两种方式都支持
执行命令
用户名和密码的方式
import paramiko # 创建SSH对象 ssh = paramiko.SSHClient() # 允许链接不在know_hosts文件中的主机 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 链接服务器 用户名和密码的方式 ssh.connect(hostname='172.16.219.173',port=22,username='root',password='jason123') # 执行命令 stdin, stdout, stderr = ssh.exec_command('ls /') """ stdin可以用来输入额外的命令 stdout,stderr 正确和错误的返回结果 """ res = stdout.read() print(res.decode('utf-8')) # 关闭链接 ssh.close()
查看虚拟机ip地址命令 ip a
公钥私钥的方式
""" 首先你得生成你本地的公钥和私钥 以mac为例 1.生成 ssh-keygen -t rsa 2.将公钥拷贝到远程服务器 ssh-copy-id -i ~/.ssh/id_rsa.pub 用户名@服务器地址 3.查看私钥 cat ~/.ssh/id_rsa """ # 公钥和私钥(先讲公钥保存到服务器上) import paramiko # 读取本地私钥 private_key = paramiko.RSAKey.from_private_key_file('a.txt') # 创建SSH对象 ssh = paramiko.SSHClient() # 允许连接不在know_hosts文件中的主机 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 连接服务器 ssh.connect(hostname='172.16.219.173', port=22, username='root', pkey=private_key) # 执行命令 stdin, stdout, stderr = ssh.exec_command('ls /') # 获取命令结果 result = stdout.read() print(result.decode('utf-8')) # 关闭连接 ssh.close()
# 用户名和密码的方式 import paramiko # 用户名和密码 transport = paramiko.Transport(('172.16.219.173', 22)) transport.connect(username='root', password='jason123') sftp = paramiko.SFTPClient.from_transport(transport) # 上传文件 sftp.put("a.txt", '/data/b.txt') # 注意上传文件到远程某个文件下 文件目录必须存在 # 下载文件 # sftp.get('远程服务器文件路径', '本地文件路径') # 将远程文件下载到本地并重新命令 transport.close() # 公钥私钥的方式 import paramiko private_key = paramiko.RSAKey.from_private_key_file('a.txt') transport = paramiko.Transport(('172.16.219.173', 22)) transport.connect(username='root', pkey=private_key) sftp = paramiko.SFTPClient.from_transport(transport) # 将location.py 上传至服务器 /tmp/test.py # sftp.put('/tmp/location.py', '/tmp/test.py') # 将remove_path 下载到本地 local_path sftp.get('/data/b.txt', 'hahaha.txt') transport.close()
思考:如果我限制即想执行命令又想上传下载文件,并且次数不限
import paramiko class SSHProxy(object): def __init__(self, hostname, port, username, password): self.hostname = hostname self.port = port self.username = username self.password = password self.transport = None def open(self): # 给对象赋值一个上传下载文件对象连接 self.transport = paramiko.Transport((self.hostname, self.port)) self.transport.connect(username=self.username, password=self.password) def command(self, cmd): # 正常执行命令的连接 至此对象内容就既有执行命令的连接又有上传下载链接 ssh = paramiko.SSHClient() ssh._transport = self.transport stdin, stdout, stderr = ssh.exec_command(cmd) result = stdout.read() return result def upload(self, local_path, remote_path): sftp = paramiko.SFTPClient.from_transport(self.transport) sftp.put(local_path, remote_path) sftp.close() def close(self): self.transport.close() # with开始时自动触发 def __enter__(self): # print('hahaha') self.open() return self # with代码块允许结束之后自动触发 def __exit__(self, exc_type, exc_val, exc_tb): self.close() # 使用类似如下 # with一个对象的时候会自动触发对象的__enter__方法 并且该方法返回什么,as后面就拿到什么 with SSHProxy('172.16.219.173',22,'root','jason123') as obj: obj.command() obj.upload()
""" 题目:请在Context类中添加代码完成该类的实现 考点:with上下文管理器 class Context: pass with Context() as ctx: ctx.do_something() """ class Context: def __enter__(self,*args,**kwargs): # 返回的就是with... as后面的对象 return self def __exit__(self,*args,**kwargs): pass def do_something(self): pass with Context() as ctx: ctx.do_something()