Flask 做爲一個全棧架構,若是你只會 python,而不懂 javascript 的前端知識,彷佛是沒法支撐起你的 web 夢想的,好比,一個簡單的頁面 局部刷新 功能,你就須要用到 ajax 的知識,固然,你還能夠使用 HTML5 的新特性 —— websocket功能,好在 flask 還提供了一個 flask-socketio 插件,本文咱們就探討一下這個 flask-scoketio插件的用法。
pip install flask-socketio
本文是在 《基於 flask 的 CRUD 操做》 的基礎上增長了 webscoket 的功能,使用的是 init_app()
的形式加載 flask-socketio 插件,和網上的大多數教程稍有不一樣。javascript
flask-wtf-crud/ |-- env/ |-- <python虛擬環境> |-- app/ <項目的模塊名稱> |-- crud/ <前端藍圖> |-- __init__.py |-- views.py <路由和視圖函數文件> |-- forms.py <表單類文件, wtforms插件必須項> |-- templates <HTML模板> |-- static <靜態文件夾> |-- js <JavaScript 文件夾> |-- crud.js # 異步請求的程序主要在此添加 |-- XXXXXX/ <其它藍圖> |-- __init__.py |-- models.py <數據庫模型文件> |-- migrations/ <數據庫表關係文件夾,Flask-Migrate遷移數據庫時使用> |-- config.py <項目的配置文件> |-- manage.py <用於啓動程序以及其它程序任務>
# -*- 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)) # 新加入的內容 if __name__ == '__main__': manager.run()
# -*- 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 # 新加入的內容 db = SQLAlchemy() async_mode = None socketio = SocketIO() def create_app(config_name): """ 使用工廠函數初始化程序實例""" 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) # 新加入的內容 # 註冊藍本crud from .crud import crud as crud_blueprint app.register_blueprint(crud_blueprint, url_prefix='/crud') return app
# -*- 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 # 新加入的內容 from flask_socketio import emit # 新加入的內容 # 新加入的內容-開始 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) \\ 每五秒發送一次 socketio.emit('user_response', {'data': users_to_json}, namespace='/websocket/user_refresh') # 新加入的內容-結束 @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/<user_id>', 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) # 新加入的內容-開始 @socketio.on('connect', namespace='/websocket/user_refresh') def connect(): """ 服務端自動發送通訊請求 """ 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): """ 服務端接受客戶端發送的通訊請求 """ emit('server_response', {'data': message['data']}) # 新加入的內容-結束
---------- 以上內容是後端的內容,如下內容是將是前段的內容 ----------html
$(document).ready(function () { namespace='/websocket/user_refresh'; var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + namespace); $("#url_show").text("websocket URL: " + location.protocol + '//' + document.domain + ':' + location.port + namespace); socket.on('connect', function() { // 發送到服務器的通訊內容 socket.emit('connect_event', {data: '我已鏈接上服務端!'}); }); socket.on('server_response', function(msg) { \\ 顯示接受到的通訊內容,包括服務器端直接發送的內容和反饋給客戶端的內容 $('#log').append('<br>' + $('<div/>').text('接收 : ' + msg.data).html()); }); socket.on('user_response', function(msg) { //console.log(eval(msg.data[0])); //$('#users_show').append('<br>' + $('<div/>').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 = "<a href=" + location.protocol + '//' + document.domain + ':' + location.port + "/crud/websocket-edit/" + value.id + "> 修改</a>"; delete_url = "<a href=\"javascript:delete_user_" + value.id + "()\">刪除</a>"; var trs = ""; trs += "<tr><th>" + (n+1) + "</th><td>" + value.username + "</td><td>" + value.email + "</td><td>" + role + "</td><td>" + status + "</td><td>" + edit_url + " | " + delete_url +"</td></tr>"; tbody += trs; }) $('#users_show').empty(); $('#users_show').append(tbody); }); });
每次打開網頁,會顯示服務端發送的內容——「試圖鏈接客戶端!」,其後,客戶端返回給服務端——「我已鏈接上服務端!」,然後又被服務端返回給客戶端顯示。前端
如下的表格內容顯示數據局裏的內容,每 5 秒局部刷新一次表格內容。java
服務器後端 log 日誌內容以下:python
源碼下載git