摘要:理解協(xié)議協(xié)議只能通過(guò)客戶端發(fā)起請(qǐng)求來(lái)與客戶端進(jìn)行通訊這是一個(gè)缺陷。與協(xié)議有著良好的兼容性。以下的表格內(nèi)容顯示數(shù)據(jù)局里的內(nèi)容,每秒局部刷新一次表格內(nèi)容。歡迎大俠能夠給我的項(xiàng)目提出修改意見(jiàn),先行感謝源碼下載參考基于的操作教程阮一峰
Flask 作為一個(gè)全棧架構(gòu),如果你只會(huì) python,而不懂 javascript 的前端知識(shí),似乎是無(wú)法支撐起你的 web 夢(mèng)想的,比如,一個(gè)簡(jiǎn)單的頁(yè)面 局部刷新 功能,你就需要用到 ajax 的知識(shí),當(dāng)然,你還可以使用 HTML5 的新特性 —— websocket功能,好在 flask 還提供了一個(gè) flask-socketio 插件,本文我們就探討一下這個(gè) flask-scoketio插件的用法。理解 websocket 協(xié)議
HTTP 協(xié)議只能通過(guò)客戶端發(fā)起請(qǐng)求來(lái)與客戶端進(jìn)行通訊 —— 這是一個(gè)缺陷。
通過(guò)websocket 協(xié)議,服務(wù)器可以主動(dòng)向客戶端推送信息,客戶端也可以主動(dòng)向服務(wù)器發(fā)送信息,是真正的雙向平等對(duì)話,屬于服務(wù)器推送技術(shù)的一種。
websocket 協(xié)議特性建立在 TCP 協(xié)議之上,服務(wù)器端的實(shí)現(xiàn)比較容易。
與 HTTP 協(xié)議有著良好的兼容性。默認(rèn)端口也是80和443,并且握手階段采用 HTTP 協(xié)議,因此握手時(shí)不容易屏蔽,能通過(guò)各種 HTTP 代理服務(wù)器。
數(shù)據(jù)格式比較輕量,性能開銷小,通信高效。
可以發(fā)送文本,也可以發(fā)送二進(jìn)制數(shù)據(jù)。
沒(méi)有同源限制,客戶端可以與任意服務(wù)器通信。
協(xié)議標(biāo)識(shí)符是ws(如果加密,則為wss),服務(wù)器網(wǎng)址就是 URL。
使用 flask-socketio 安裝插件pip install flask-socketio項(xiàng)目結(jié)構(gòu)
本文是在 《基于 flask 的 CRUD 操作》 的基礎(chǔ)上增加了 webscoket 的功能,使用的是 init_app() 的形式加載 flask-socketio 插件,和網(wǎng)上的大多數(shù)教程稍有不同。
flask-wtf-crud/ |-- env/ |--將 flask-socketio 引入項(xiàng)目 修改 manage.py 內(nèi)容|-- app/ <項(xiàng)目的模塊名稱> |-- crud/ <前端藍(lán)圖> |-- __init__.py |-- views.py <路由和視圖函數(shù)文件> |-- forms.py <表單類文件, wtforms插件必須項(xiàng)> |-- templates |-- static <靜態(tài)文件夾> |-- js |-- crud.js # 異步請(qǐng)求的程序主要在此添加 |-- XXXXXX/ <其它藍(lán)圖> |-- __init__.py |-- models.py <數(shù)據(jù)庫(kù)模型文件> |-- migrations/ <數(shù)據(jù)庫(kù)表關(guān)系文件夾,Flask-Migrate遷移數(shù)據(jù)庫(kù)時(shí)使用> |-- config.py <項(xiàng)目的配置文件> |-- manage.py <用于啟動(dòng)程序以及其它程序任務(wù)>
# -*- coding:utf-8 -*- __author__ = "東方鶚" __blog__ = u"http://www.os373.cn" import os from app import create_app, db, socketio from app.models import User from flask_script import Manager, Shell from flask_migrate import Migrate, MigrateCommand app = create_app(os.getenv("FLASK_CONFIG") or "default") manager = Manager(app=app) migrate = Migrate(app=app, db=db) def make_shell_context(): return dict(app=app, db=db, User=User) manager.add_command("shell", Shell(make_context=make_shell_context)) manager.add_command("db", MigrateCommand) manager.add_command("run", socketio.run(app=app, host="0.0.0.0", port=5001)) # 新加入的內(nèi)容 if __name__ == "__main__": manager.run()修改 app/__init__.py 內(nèi)容
# -*- coding:utf-8 -*- __author__ = "東方鶚" __blog__ = u"http://www.os373.cn" from flask import Flask from flask_sqlalchemy import SQLAlchemy from config import config from flask_socketio import SocketIO # 新加入的內(nèi)容 db = SQLAlchemy() async_mode = None socketio = SocketIO() def create_app(config_name): """ 使用工廠函數(shù)初始化程序?qū)嵗?"" app = Flask(__name__) app.config.from_object(config[config_name]) config[config_name].init_app(app=app) db.init_app(app=app) socketio.init_app(app=app, async_mode=async_mode) # 新加入的內(nèi)容 # 注冊(cè)藍(lán)本crud from .crud import crud as crud_blueprint app.register_blueprint(crud_blueprint, url_prefix="/crud") return app當(dāng)前藍(lán)圖的 views.py
# -*- coding:utf-8 -*- __author__ = "東方鶚" __blog__ = u"http://www.os373.cn" from flask import render_template, redirect, request, current_app, url_for, flash, json from . import crud from ..models import User from .forms import AddUserForm, DeleteUserForm, EditUserForm from ..import db from threading import Lock from app import socketio # 新加入的內(nèi)容 from flask_socketio import emit # 新加入的內(nèi)容 # 新加入的內(nèi)容-開始 thread = None thread_lock = Lock() def background_thread(users_to_json): """Example of how to send server generated events to clients.""" while True: socketio.sleep(5) 每五秒發(fā)送一次 socketio.emit("user_response", {"data": users_to_json}, namespace="/websocket/user_refresh") # 新加入的內(nèi)容-結(jié)束 @crud.route("/", methods=["GET", "POST"]) def index(): return render_template("index.html") @crud.route("/websocket", methods=["GET", "POST"]) def websocket(): add_user_form = AddUserForm(prefix="add_user") delete_user_form = DeleteUserForm(prefix="delete_user") if add_user_form.validate_on_submit(): if add_user_form.role.data == u"True": role = True else: role = False if add_user_form.status.data == u"True": status = True else: status = False u = User(username=add_user_form.username.data.strip(), email=add_user_form.email.data.strip(), role=role, status=status) db.session.add(u) flash({"success": u"添加用戶<%s>成功!" % add_user_form.username.data.strip()}) if delete_user_form.validate_on_submit(): u = User.query.get_or_404(int(delete_user_form.user_id.data.strip())) db.session.delete(u) flash({"success": u"刪除用戶<%s>成功!" % u.username}) users = User.query.all() return render_template("websocket.html", users=users, addUserForm=add_user_form, deleteUserForm=delete_user_form) @crud.route("/websocket-edit/", methods=["GET", "POST"]) def user_edit(user_id): user = User.query.get_or_404(user_id) edit_user_form = EditUserForm(prefix="edit_user", obj=user) if edit_user_form.validate_on_submit(): user.username = edit_user_form.username.data.strip() user.email = edit_user_form.email.data.strip() if edit_user_form.role.data == u"True": user.role = True else: user.role = False if edit_user_form.status.data == u"True": user.status = True else: user.status = False flash({"success": u"用戶資料已修改成功!"}) return redirect(url_for(".basic")) return render_template("edit_websocket.html", editUserForm=edit_user_form, user=user) # 新加入的內(nèi)容-開始 @socketio.on("connect", namespace="/websocket/user_refresh") def connect(): """ 服務(wù)端自動(dòng)發(fā)送通信請(qǐng)求 """ global thread with thread_lock: users = User.query.all() users_to_json = [user.to_json() for user in users] if thread is None: thread = socketio.start_background_task(background_thread, (users_to_json, )) emit("server_response", {"data": "試圖連接客戶端!"}) @socketio.on("connect_event", namespace="/websocket/user_refresh") def refresh_message(message): """ 服務(wù)端接受客戶端發(fā)送的通信請(qǐng)求 """ emit("server_response", {"data": message["data"]}) # 新加入的內(nèi)容-結(jié)束
---------- 以上內(nèi)容是后端的內(nèi)容,以下內(nèi)容是將是前段的內(nèi)容 ----------
crud.js 內(nèi)容$(document).ready(function () { namespace="/websocket/user_refresh"; var socket = io.connect(location.protocol + "http://" + document.domain + ":" + location.port + namespace); $("#url_show").text("websocket URL: " + location.protocol + "http://" + document.domain + ":" + location.port + namespace); socket.on("connect", function() { // 發(fā)送到服務(wù)器的通信內(nèi)容 socket.emit("connect_event", {data: "我已連接上服務(wù)端!"}); }); socket.on("server_response", function(msg) { 顯示接受到的通信內(nèi)容,包括服務(wù)器端直接發(fā)送的內(nèi)容和反饋給客戶端的內(nèi)容 $("#log").append("顯示結(jié)果
" + $("").text("接收 : " + msg.data).html()); }); socket.on("user_response", function(msg) { //console.log(eval(msg.data[0])); //$("#users_show").append("
" + $("").text("接收 : " + msg.data).html()); var tbody = ""; var obj = eval(msg.data[0]); $.each(obj, function (n, value) { var role = ""; if (value.role===true){ role = "管理員"; }else { role = "一般用戶"; } var status = ""; if (value.status===true){ status = "正常"; }else { status = "注銷"; } edit_url = " 修改"; delete_url = "刪除"; var trs = ""; trs += ""; tbody += trs; }) $("#users_show").empty(); $("#users_show").append(tbody); }); }); " + (n+1) + " " + value.username + " " + value.email + " " + role + " " + status + " " + edit_url + " | " + delete_url +"
每次打開網(wǎng)頁(yè),會(huì)顯示服務(wù)端發(fā)送的內(nèi)容——“試圖連接客戶端!”,其后,客戶端返回給服務(wù)端——“我已連接上服務(wù)端!”,而后又被服務(wù)端返回給客戶端顯示。
以下的表格內(nèi)容顯示數(shù)據(jù)局里的內(nèi)容,每 5 秒局部刷新一次表格內(nèi)容。
服務(wù)器后端 log 日志內(nèi)容如下:
總結(jié)由于 flask 架構(gòu)具有上下文的限制,在數(shù)據(jù)庫(kù)里 增加刪改 內(nèi)容的時(shí)候,表格的內(nèi)容沒(méi)有變化——盡管局部已經(jīng)進(jìn)行了刷新。要想顯示變化后的數(shù)據(jù)庫(kù)內(nèi)容,必須得重新啟動(dòng)一下 flask 服務(wù)。
就整體的部署來(lái)說(shuō),在 flask 項(xiàng)目里添加 websocket 協(xié)議,顯得項(xiàng)目較重,實(shí)現(xiàn)一個(gè)局部刷新的功能還是用 ajax 比較簡(jiǎn)單。
歡迎大俠能夠給我的項(xiàng)目提出修改意見(jiàn),先行感謝!?。?/p>
源碼下載
參考基于 flask 的 CRUD 操作
WebSocket 教程 —— 阮一峰
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/44491.html
摘要:將用戶命令通過(guò)接口傳送給,從而進(jìn)行資源的增刪改等操作。要使用編寫應(yīng)用程序,當(dāng)下大多語(yǔ)言都可以很方便地去實(shí)現(xiàn)請(qǐng)求來(lái)操作的接口從而控制和查詢資源,但本文主要是利用已有的客戶端來(lái)更加優(yōu)雅地實(shí)現(xiàn)的資源控制。 showImg(https://segmentfault.com/img/remote/1460000013517345); 【利用K8S技術(shù)棧打造個(gè)人私有云系列文章目錄】 利用K8S...
摘要:將用戶命令通過(guò)接口傳送給,從而進(jìn)行資源的增刪改等操作。要使用編寫應(yīng)用程序,當(dāng)下大多語(yǔ)言都可以很方便地去實(shí)現(xiàn)請(qǐng)求來(lái)操作的接口從而控制和查詢資源,但本文主要是利用已有的客戶端來(lái)更加優(yōu)雅地實(shí)現(xiàn)的資源控制。 showImg(https://segmentfault.com/img/remote/1460000013517345); 【利用K8S技術(shù)棧打造個(gè)人私有云系列文章目錄】 利用K8S...
摘要:目的曾經(jīng)想向前臺(tái)實(shí)時(shí)返回任務(wù)的狀態(tài)監(jiān)控,也查看了很多博客,但是好多也沒(méi)能如愿,因此基于網(wǎng)上已有的博客已經(jīng)自己的嘗試,寫了一個(gè)小的,實(shí)現(xiàn)前臺(tái)實(shí)時(shí)獲取后臺(tái)傳輸?shù)娜蝿?wù)狀態(tài)。實(shí)現(xiàn)仿照其他例子實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的后臺(tái)任務(wù)監(jiān)控。 1. 目的曾經(jīng)想向前臺(tái)實(shí)時(shí)返回Celery任務(wù)的狀態(tài)監(jiān)控,也查看了很多博客,但是好多也沒(méi)能如愿,因此基于網(wǎng)上已有的博客已經(jīng)自己的嘗試,寫了一個(gè)小的demo,實(shí)現(xiàn)前臺(tái)實(shí)時(shí)獲取后...
摘要:然而代碼的最終執(zhí)行結(jié)果表明,內(nèi)的代碼運(yùn)行應(yīng)該是先于里面的代碼。項(xiàng)目中請(qǐng)求服務(wù)端異步獲取數(shù)據(jù)的接口參考文檔中幾種的區(qū)別源碼閱讀啟動(dòng)過(guò)程 公司一些管理后臺(tái)的前端頁(yè)面,使用的是angular開發(fā)的,得益于angular的雙向綁定和模塊化controller使得構(gòu)建pc端的CRUD應(yīng)用簡(jiǎn)單了不少。angular有很多比較難理解的概念,上手起來(lái)沒(méi)有vue簡(jiǎn)單,不過(guò)對(duì)著模板項(xiàng)目、看看tutoria...
摘要:生成屬性這一步,我們要先提取原函數(shù)中的的對(duì)象。所以這里我們還是主要使用來(lái)訪問(wèn)節(jié)點(diǎn)獲取第一級(jí)的,也就是函數(shù)體將合并的寫法用生成生成生成插入到原函數(shù)下方刪除原函數(shù)程序輸出將中的屬性提升一級(jí)這里遍歷中的屬性沒(méi)有再采用,因?yàn)檫@里結(jié)構(gòu)是固定的。 ??經(jīng)過(guò)之前的三篇文章介紹,AST的CRUD都已經(jīng)完成。下面主要通過(guò)vue轉(zhuǎn)小程序過(guò)程中需要用到的部分關(guān)鍵技術(shù)來(lái)實(shí)戰(zhàn)。 下面的例子的核心代碼依然是最簡(jiǎn)單...
閱讀 2542·2021-10-11 10:58
閱讀 1020·2019-08-29 13:58
閱讀 1661·2019-08-26 13:32
閱讀 830·2019-08-26 10:40
閱讀 3256·2019-08-26 10:18
閱讀 1756·2019-08-23 14:18
閱讀 1106·2019-08-23 10:54
閱讀 435·2019-08-22 18:39