Django基于channel与channel_layers实现群聊功能、gojs插件、paramiko模块
1. Django基于channel实现群聊功能
首先我们使用pycharm创建的Django项目都会自带的templates文件夹并且是全局的,我们可以手动的在每一个应用下创建templates模板文件夹,其实每一个应用下都可以有自己的urls.py(路由分发),views.py,templates,static
如果多个应用下有和全局都有模板文件夹,会优先查找全局的,如果全局没有,则按照文件注册的app顺序从上往下查找查找对应的文件
1. 我们在routing.py中配置了以下代码后就会使Django支持HTTP和websocket协议的原因是自己加的http

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
from channels.routing import ProtocolTypeRouter,URLRouter from app01 import consumers from django.conf.urls import url application = ProtocolTypeRouter({ 'websocket':URLRouter([ # 书写websocket路由与视图函数对应关系 url('^chat/', consumers.ChatConsumer) ]) })
2. 新建视图函数文件consumers.py文件中后端有三个方法:
from channels.generic.websocket import WebsocketConsumer from channels.exceptions import StopConsumer consumer_object_list = [] class ChatConsumer(WebsocketConsumer): def websocket_connect(self, message): """客户端请求建立链接时 自动触发""" 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): """客户端断开链接之后 自动触发""" # 客户端断开链接之后 应该将当前对象移除 consumer_object_list.remove(self) raise StopConsumer()
3. 前端有四个方法在js中写
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() }
2. channel_layers模块使用
1. 配置文件中配置参数
CHANNEL_LAYERS = { 'default':{ 'BACKEND':'channels.layers.InMemoryChannelLayer' } }
2. 如何获取无名有名分组中url携带的参数
# 获取routing中有名无名分组的参数 self.scope是一个大字典 里面包含了前端所有的数据 # task_id = self.scope['url_route']['args'].get('task_id') task_id = self.scope['url_route']['kwargs'].get('task_id') # print(type(task_id)) # <class 'str'>
3. 链接对象自动加入对应的群聊
from asgiref.sync import async_to_sync # 固定写法 将用户添加到群聊中, 第一个是群号字符串类型,第二个是唯一标识 async_to_sync(self.channel_layer.group_add)(task_id,self.channel_name)
4. 给特定的群中发消息
async_to_sync(self.channel_layer.group_send)(task_id,{'type':'my.send','message':{'code':'init','data':node_list}}) """ 后面字典的键是固定的 就叫type和message type后面指定的值就是负责发送消息的方法(将message后面的数据交由type后面指定的方法发送给对应的群聊中) 针对type后面的方法名 有一个固定的变化格式 my.send >>> my_send xxx.ooo >>> xxx_ooo """
5. 在类中需要定义一个专门发送消息的方法
def my_send(self,event): message = event.get('message') # {'code':'init','data':node_list} # 发送数据 self.send(json.dumps(message)) """ 内部原理就类似于是循环当前群组里面所有的链接对象 然后依次执行send方法 for self in self_list: self.send """
6. 断开链接之后去对应的群聊剔除群成员
async_to_sync(self.channel_layer.group_discard)(task_id,self.channel_name)
3. gojs插件
是用来通过代码动态展示图表数据关系的,比如组织架构图,执行流程图等等,是前端js代码
如果使用需要下载他的文件引入
我所了解的有三个文件:
1.go.js
是使用gojs所必须要导入的js文件
2.go-debug.js
可以帮你打印一些bug日志 正常线上不会使用
3.Figures.js
go.js中自带了一些基本的图标,额外扩展的图标需要引入该文件
1. 基本使用
gojs使用基本套路是先在页面上写一个div站地方,之后初始化该div,再然后所有的图标渲染都在该div内进行!!!
<div id="myDiagramDiv" style="width: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>
创建节点有以下几种:
-
-
Shape 创建图形
-
Node 节点(结合文本与图形)
-
官网上intro有详细介绍及代码
2. 如何做到数据的前后端交互,图标的动态修改
<div id="diagramDiv" style="width: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>
3. 如何去水印
我们发现生成后的页面都有水印,因此我们可以修改go.js源码来去除水印
1. 需要找到一个特定的字符串注释该字符串所在的一行代码
/*a.kr=b.V[Ra("7eba17a4ca3b1a8346")][Ra("78a118b7")](b.V,Jk,4,4);*/
2. 在后边添加一行新代码
a.kr=function(){return false};
4. paramiko模块
通过Python代码ssh远程链接服务器并执行响应的操作,类似于XShell
1. 安装:
pip3 install paramiko
2. 基本使用
-
用户名和密码
-
公钥私钥的方式
用户名密码方式链接
import paramiko # 创建ssh对象 ssh = paramiko.SSHClient() # 允许连接不在know_hosts文件的主机 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 链接服务器 ssh.connect(hostname='10.0.0.201', port=22, username='root', password='123456') # 执行命名 stdin, stdout, stderr = ssh.exec_command('ls /') """ stdin: 输入额外命令的 stdout, stderr:输入正确和错误结果的 """ # 输出结果 二进制的 res = stdout.read() print(res.decode('utf8'))
# 关闭链接
ssh.close()
公钥私钥方式链接
注意:window系统,需要在git bash下使用 生成和查看公钥私钥
1.生成
ssh-keygen -t rsa -C "*@*.com" # 邮箱可以随意填写
2.将公钥拷贝到远程服务器
ssh-copy-id -i ~/.ssh/id_rsa.pub 服务器用户名@服务器地址
3.查看私钥
cat ~/.ssh/id_rsa
一般生成的公钥私钥在该路径下:C:\Users\lenovo\.ssh
# 公钥和私钥(先将公钥拷贝到远程服务器上 ssh-copy-id -i ~/.ssh/id_rsa.pub) import paramiko # 读取本地私钥路径,一般将私钥拷贝出来 private_key = paramiko.RSAKey.from_private_key_file('id_rsa') # 创建SSH对象 ssh = paramiko.SSHClient() # 允许连接不在know_hosts文件中的主机 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 连接服务器 ssh.connect(hostname='10.0.0.201', port=22, username='root', pkey=private_key) # 执行命令 stdin, stdout, stderr = ssh.exec_command('ls /') # 获取命令结果 result = stdout.read() print(result.decode('utf-8')) # 关闭连接 ssh.close()
3. 上传下载文件
1. 用户名密码方式
# 用户名和密码的方式 import paramiko # 用户名和密码 transport = paramiko.Transport(('10.0.0.201', 22)) transport.connect(username='root', password='123456') sftp = paramiko.SFTPClient.from_transport(transport) # 上传文件 sftp.put("a.txt", '/data/b.txt') # 注意上传文件到远程某个文件下 文件目录必须存在 # 下载文件 # sftp.get('远程服务器文件路径', '本地文件路径') # 将远程文件下载到本地并重新命令 transport.close()
2. 公钥私钥方式
# 公钥私钥的方式 import paramiko # 读取本地私钥路径 private_key = paramiko.RSAKey.from_private_key_file('id_rsa') # 创建SSH对象 transport = paramiko.Transport(('10.0.0.201', 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()
4. 封装命令与上传下载文件

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('10.0.0.201',22,'root','123456') as obj: obj.command() obj.upload()
文件上下文管理with原理:
with一个对象的时候会自动触发__enter__方法,并且return什么 as 后面就拿到什么,
with代码块结束的时候触发__exit__方法
""" 题目: 请在Context类中添加代码完成该类的实现 class Context: pass with Context() as ctx: ctx.do_something() """ class Context:
# with对象的时候触发,返回self def __enter__(self,*args,**kwargs): return self # with结束后触发 def __exit__(self,*args,**kwargs): pass def do_something(self): pass with Context() as ctx: ctx.do_something()