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()