结合manage.py,在flask项目中使用websocket模块--- flask-socketio
前言:
- 为什么我要使用 flask-socketio模块,而不是flask-sockets?
- 因为flask-socketio与前端流行的websocket库socketio语法类似,前后端交互容易理解,并且flask-socketio能非常容易得与flask项目结合。
效果预览:
后端数字更改,自动推送到前端
1.安装 flask-socketio
pip install flask-socketio
2.项目结构
myproject/ |-- env/ |-- <python虚拟环境> |-- report/ <项目的模块名称>
|-- api_1/ <接口蓝图>
|-- __init__.py
|-- views.py |-- main/ <前端蓝图> |-- __init__.py |-- views.py <路由和视图函数文件> ----------------------------------> 要增加websocket相关代码 |-- forms.py <表单类文件, wtforms插件必须项> |-- templates <HTML模板> |-- static <静态文件夹>
|-- index.html <前端页面> ------------------------------------> 要增加websocket相关代码 |-- XXXXXX/ <其它蓝图> |-- __init__.py ----------------------------------------------------> 要增加websocket相关代码 |-- models.py <数据库模型文件> |-- migrations/ <数据库表关系文件夹,Flask-Migrate迁移数据库时使用> |-- config.py <项目的配置文件> |-- manage.py <用于启动程序以及其它程序任务> --------------------------------> 要增加websocket相关代码
3.修改代码
1) 修改 myproject/report/__init__.py
# encoding: utf-8 from flask import Flask from flask_mail import Mail from flask_moment import Moment from flask_sqlalchemy import SQLAlchemy from config import config from flask_restful import Api from flask_cors import CORS # 解决跨域请求 from flask_jwt_extended import JWTManager from flask_socketio import SocketIO # 新添加的代码 mail = Mail() moment = Moment() db = SQLAlchemy() api = Api() async_mode = None # 新添加的代码 socketio = SocketIO() # 新添加的代码 from report.api_1.views import ServiceCheckApi, GetRecordData# 为了避免循环引用问题,在这里导入 # 初始化app def app_create(config_name): app = Flask(__name__) app.config.from_object(config[config_name]) config[config_name].init_app(app) mail.init_app(app) moment.init_app(app) db.init_app(app) jwt = JWTManager(app) # 路由和其他处理程序定义 # 注册蓝图 from .main import main as main_blueprint # 从当前目录下面的main子目录中导入main对象 app.register_blueprint(main_blueprint) api.add_resource(ServiceCheckApi, '/api/service_check') # api与websocket无关 api.add_resource(GetRecordData, '/api/get_data') # add_resource 函数使用指定的endpoint 将路由注册到框架上 api.init_app(app) # api初始化必须放在路由注册之后 CORS(app) # 跨域请求 socketio.init_app(app=app, async_mode=async_mode) # 新添加的代码 return app
2) 修改 myproject/manage.py
# encoding: utf-8 import os from flask_script import Manager from report import app_create from flask_migrate import Migrate, MigrateCommand from report import db, socketio # 新添加代码 app = app_create(os.getenv('FLASK_CONFIG') or 'default') # 设置启动方式,可选:development、testing、production manager = Manager(app) migrate = Migrate(app, db) # 使用Migrate将app与db关联 # 自定义命令 , # 在命令行使用: python manage.py runserver # @manager.command # def runserver(): # print('running') # 添加额外二级命令 # 第一种方式:自定义命令 # manager.add_command('db',DBmanager) # 'db'是自定义的命令名字 # 在命令行使用: python manage.py db init ,init是自定义的函数 # 第二种方式:数据迁移使用MigrateCommand中自带的命令(常用) # 该模块中带有的命令的使用顺序(顺序不能乱): # python manage.py db init # python manage.py db migrate # python manage.py db upgrade manager.add_command('db', MigrateCommand) manager.add_command('run', socketio.run(app=app, host='0.0.0.0', port=5000)) # 新加入的代码,重写manager的run命令 if __name__ == '__main__': manager.run()
3)修改 myproject/report/main/views.py
# encoding: utf-8 import re import requests from . import main from flask import render_template,redirect, url_forfrom report import socketio import time from flask_socketio import emit # 新加入的代码 from threading import Lock import random # 新加入的代码-开始 thread = None thread_lock = Lock() def background_thread(users_to_json): """Example of how to send server generated events to clients.""" while True: print(users_to_json) users_to_json = [{'name': '王腾' + str(random.randint(1, 100))}] socketio.sleep(0.5) # 每五秒发送一次 socketio.emit('user_response', {'data': users_to_json}, namespace='/websocket/user_refresh') @socketio.on('connect', namespace='/websocket/user_refresh') def connect(): """ 服务端自动发送通信请求 """ global thread user_to_json = '' with thread_lock: 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']}) # 新加入的代码-结束 @main.route('/', methods=['GET']) def index(): return render_template('index.html')
4)修改myproject/report/main/templats/index.html
需要注意到是:
server_response、
user_response、
connect、
connect_event
这几个自定义事件在前后端的对应关系。emit是发送消息,on是接收消息
<!doctype html> <html> <head> <meta charset="utf-8"> <meta http-equiv="x-ua-compatible" content="ie=edge"> <title>xxx</title> <meta name="description" content=""> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 引入css文件 start--> <link href="https://cdn.bootcss.com/bootstrap/4.1.0/css/bootstrap.css" rel="stylesheet"> <!-- 引入css文件 end--> </head> <body style="margin: 0 auto"> <div class="container"> <div class="row"> <div style="margin-bottom: 5%" class="col-md-12 text-center head-title-font"> <button id="auto_num" class="btn btn-primary" style="width: 10%"></button> </div> </div> </div> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.slim.js"></script> <!-- 新添加代码 start --> <script src="https://cdn.bootcss.com/socket.io/2.1.1/socket.io.dev.js"></script> <script> var socket = io('http://127.0.0.1:5000/websocket/user_refresh');--------------------------> 后台接口 socket.on('connect', function() { // 发送到服务器的通信内容 socket.emit('connect_event', {data: '我已连接上服务端!'}); }); socket.on('server_response', function(msg) { //显示接受到的通信内容,包括服务器端直接发送的内容和反馈给客户端的内容 console.log(msg); }); socket.on('user_response', function(msg) { // 获取后端传过来的业务数据 var obj = eval(msg.data[0]); console.log(obj); $("#auto_num").empty(); $("#auto_num").append(obj['name']); }); </script>
<!-- 新添加代码 end -->
</body> </html>
4. 项目启动
在项目目录下,执行:
python manage.py runserver
或者与gunicorn、uWSGI等结合使用。