flask
文章目录
教程
工具 list
工具列表
插件
flask + pydantic 序列化和参数验证
Cheat Sheat
构建路由(注册函数)
- app.route('/')
创建 URL
- url_for
处理模板
- render_template
文件夹
static
静态文件
- 存放 css、javascript 文件等
url 生成
1url_for('static', filename='style.css')
templates
模板文件
- 存放 html 模板
处理模板
1render_template('hello.html', name=name)
配置
app.config
sqlalchemy
https://flask-sqlalchemy.palletsprojects.com/en/2.x/quickstart/
表 db.Model
单个表
| |
多个表
https://www.cnblogs.com/liangmingshen/p/9769975.html
| |
关联方法
- 当前表:db.relationship 声明关联的表
- 关联的表:外键 ForeignKey
当前表 链接 关联表
- user.address
关联表 链接 当前表
- address.user
注意:
这里必须使用 backref 了
1 2 3 4addresses = db.relationship("Address", backref="user") # * 高级用法 addresses = db.relationship("Address", backref=db.backref("users", lazy=True))
backref
https://www.cnblogs.com/liangmingshen/p/9769975.html
backref 用于在关系另一端的类中快捷地创建一个指向当前类对象的属性, 而当需要对那个属性指定参数时使用 db.backref()。
操作
创建记录
- admin = User(username='admin', email='admin@example.com')
修改与提交
在表上修改
- address.users.append(lucy)
在数据库上修改
- db.session.add(user)
- db.session.commit()
查询
User.query.all()
- 查询整个表
User.query.filter_by(username='admin').first()
- 查询过滤
加载问题
对于 lazy-loading
需要在必要时加载关联的 backref 表
1 2 3 4 5>>> from sqlalchemy.orm import joinedload >>> query = Category.query.options(joinedload('posts')) >>> for category in query: ... print category, category.posts <Category u'Python'> [<Post u'Hello Python!'>, <Post u'Snakes'>]
路由 Routing
https://flask.palletsprojects.com/en/1.1.x/quickstart/#routing
https://flask.palletsprojects.com/en/1.1.x/api/#url-route-registrations 官方文档
https://www.cnblogs.com/amyleell/articles/9178596.html 路由的工作原理
经典路由 canonical URL
- 经典路由都是一 “/” 结尾
@app.route()装饰器 与 app.add_url_rule() 方法
- 可以相互替代
共有参数
rule
- URL 规则,路由路径
view_func
- 视图函数名称,用来处理路径的函数
defaults=None
- 默认值, url 的缺省默认值,当 URL 中无参数,函数需要参数时,使用 defaults={'k':'v'}为函数提供参数
endpoint=None
- 名称,用于反向生成 URL,即: url_for('名称')
methods=None
- 允许的请求方式,如:["GET","POST"]
strict_slashes=None
- 对 URL 最后的 / 符号是否严格要求
- 有没有最有的‘/’,是否有区别
- redirect_to=None
- subdomain=None
1 2 3 4def index(): return "Index" self.add_url_rule(rule='/index.html', endpoint="index", view_func=index, methods=["GET","POST"])1 2app.add_url_rule(rule='/index.html', endpoint="index", view_func=index, methods=["GET","POST"]) app.view_functions['index'] = index1 2 3 4 5 6 7 8 9 10 11 12def auth(func): def inner(*args, **kwargs): print('before') result = func(*args, **kwargs) print('after') return result return inner @app.route('/index.html', methods=['GET', 'POST'], endpoint='index') @auth def index(): return 'Index'
@app.route() 的 被修饰函数 与 app.add_url_rule 的 view_functions 一致
端点 endpoint 与 视图函数 view_function
https://www.cnblogs.com/amyleell/articles/9178596.html
路由的处理顺序
- Url –> endpoint –> view_function
- 一般情况 endpoint 与 view_function 一致
末尾 '/' 的处理
程序的 rule='xxx', 带有 '/'
用户输入路径
- 末尾不带 '/'
程序自动转换成带有 '/' 的 rule
- 末尾带 '/'
- 正常处理
程序的 rule='xxx', 不带 ''
用户输入路径
- 末尾不带 '/'
正常处理
- 末尾带 '/'
- 发生 404 错误
同一个 view_func, 多个 rule
| |
class View
https://flask.palletsprojects.com/en/1.1.x/views/
- 属性和方法有规定名称和样式
使用方法
使用 class method
- your_view.as_view('your_view')
flask.views.View 基类
flask.views.MethodView
Request Context
针对一个 request context
- 即环境,包含各种变量,这些对于一个 request 都是全局的
- 但是针对不同的 Request 是隔离的
手动测试 request context 的方法
https://flask.palletsprojects.com/en/1.1.x/reqcontext/
app.test_request_context()
1 2 3 4 5 6 7def generate_report(year): format = request.args.get('format') ... with app.test_request_context( '/make_report/2017', data={'format': 'short'}): generate_report()
一个 request 的处理过程,相关的函数
https://flask.palletsprojects.com/en/1.1.x/reqcontext/#callbacks-and-errors
按顺序排列
before_request()
- 这是多个函数
- 但是只会最多触发一个
- 如果触发,并返回一个 Response, 则跳过 view_func
view_func
- 视图函数
- 处理 request, 并返回一个 Response
after_request()
- 多个函数
- 修改前面生成的 Response, 或者制作一个全新的 Response
errorhandler()
- 如果在 after_request() 之中,或之前发生异常,会被调用
- 产生一个异常处理的 Response
- 如果没有手动设置,则生成 500 Internal Server Error Response.
teardown_request() 和 teardown_appcontext()
- 都是多个函数
- 即使发生异常也会被调用
回调函数
teardown_request
栈弹出 RequestContext, 回调函数
- @app.teardown_request
| |
errorhandler
异常回调函数
@app.errorhandler(error_code)
- eg: @app.errorhandler(404)
1 2 3 4 5from flask import render_template @app.errorhandler(404) def page_not_found(error): return render_template('page_not_found.html'), 404
after_request
@after_this_request
用来修饰 view_func 生成的 Response https://flask.palletsprojects.com/en/1.1.x/api/#flask.after_this_request
| |
手动测试 flask
app.test_client()
| |
app.test_request_context
| |
flask Request
https://flask.palletsprojects.com/en/1.1.x/quickstart/#accessing-request-data
| |
直接访问 flask.requset 对象的属性
- request.method == 'POST'
request methods
- request.method
requset 参数
request.args
- 一个 dict 对象
request 上传的文件 files
- request.files
flask Response
https://flask.palletsprojects.com/en/1.1.x/quickstart/#about-responses
返回值,转换成 Response 对象
jsonify 与 json
把 dict 转换成 Response
- 使用 jsonify(your_dict), 把 your_dict, 转换成 Response
- 这种转换,是 flask 自动完成的,当返回值是 dict 类型时,不需要具体 手动调用 jsonify
也可用于非 dict 类型
| |
转换规则
本身就是 Response 对象
- 不作处理
string, 字符串
- 使用默认参数,转换成 Response
dict
- 使用 jsonify
tuple
- 可以额外提供更多信息
- 但是要有一定的格式
格式
- (response, status)
- (response, headers)
- (response, status, headers)
解说
- status
- status_code
- 将会替换掉 Response 中的 status_code
headers
- list or dict
其它
- If none of that works, Flask will assume the return value is a valid WSGI application and convert that into a response object.
- 当作 WSGI application, 并转换成 Response
自己制作 Response object
模板
flask.render_template(filename, **context)
| |
template: 第一个参数
- html 模板
- 注意:
- 这里是 template 文件夹下的文件
- 如果是 list, 使用第一个有效的
**context: 其余参数- 以关键字参数的形式,指定模板中出现的变量
flask.render_template_string(template_string, **context)
生成 Response
个性化配置 Response
flask.make_response(*args)
用法样式
- make_response()
生成空 Response()
- make_response(content)
- make_response(content, status_code)
| |
flask Sessions 会话
用于跨 Request 数据共享,对于同一个用户
- 基于 cookies
- 和 signs the cookies cryptographically(cookies 加密) https://flask.palletsprojects.com/en/1.1.x/quickstart/#sessions https://flask.palletsprojects.com/en/1.1.x/api/#sessions
权限问题
- 用户没有 secret key 密钥,只能查询,不能修改 cookie
- 使用 sessions, 必须设置 secret key
使用
- 设置 app.secret_key
把 session 当作 dict 使用
- session['username']
| |
密钥制作工具
| |
html 转义
把一些特殊字符(&, <, >, ', ")等,转换成 html 能识别的字符
flask redirect 跳转
https://flask.palletsprojects.com/en/1.1.x/quickstart/#redirects-and-errors
| |
flask.redirect(location, code=302, Response=None)
返回 Response, 把客户端引导到 location
flask.url_for(endpoint, **values)
制作一个 url
flask Blueprint
用来组织 flask views
原来
- app –> views
现在
- app –> Blueprint –> views
特征
- 有些类似 app, 但又不是 app
适合大型项目
- 模块化
- 结构化
- 可以用 Blueprint 提供 template filters, static files, templates and other utilities
- 一个 Blueprint 可以没有 view funcions
- 共享 app 的配置
绑定位置
- 一个 URL prefix 或 '/'
- 子域名 subdomain
特别的
- 可以在不同的 URL rule 上,重复绑定
定义 Blueprint
定义:
- your_blu = Blueprint(name: str, import_name: str, static_folder_name: str)
route 使用
- @your_blu.route()
:定义 Blueprint:
| |
:END:
绑定到 app
app.register_blueprint(your_blu)
- 绑定到 '/your_blu.name'
app.register_blueprint(simple_page, url_prefix='/given_pages')
- 绑定到 '/given_pages'
| |
资源文件夹
- 一般是 "项目目录/blueprint_name"
Resource folder 资源文件夹
- your_blu.open_resource()
| |
Static Files
Blueprint 拥有 url_prefix='/given_pages'
文件夹: /your_blu.url_prefix/static_folder_name
1 2admin = Blueprint('admin', __name__, static_folder='static') # static folder --> /admin/static没有 url_prefix
- 获取不到 static files 文件夹
- 因为 /static 属于整个 app, 优先级更高(冲突)
Templates
| |
Building URLs
- 使用 url_for
其它 Blueprint
以 "." 分割 Blueprint 名称 和 end_point
1url_for('admin.index')
当前 Blueprint
以 "." 开头 + end_point
1url_for('admin.index')
flask flash
flask(message: str) 把消息存储到 session 中,在模板中使用 get_flashed_message() 获取生成的列表
工作原理
- flask 把数据存储到 session 中
view_func 返回一个模板 或 跳转到一个返回模板的 view_func
- eg: a) return Your_temp b) return redirect(url_for('hello'))
在模板中取出,之前存储的数据
- 使用 get_flashed_message()提取
flask instance
instance folder
https://flask.palletsprojects.com/en/0.12.x/config/#instance-folders
作用
- 用来规避 VCS 软件的版本控制
- 放置配置文件
可以自己指定 路径, 也可以是默认值
注意:自己指定,必须是绝对路径
1app = Flask(__name__, instance_path='/path/to/instance/folder')
默认值
项目根目录
/myapp.py /instance
Flask app 与 instance_relative_config
是否配置文件查询路径在 instance 文件夹
| |
Flask.open_instance_resource()
打开 instance 文件夹下文件的快捷方式
| |
flask sqlite3
https://flask.palletsprojects.com/en/1.1.x/tutorial/database/
连接
- flask.g.db = sqlite3.connect
相关变量
flask.g
https://flask.palletsprojects.com/en/1.1.x/api/#flask.g 跨 Application Context 的变量
- 用来存储每个 request 共有的变量
关闭
- db.close()
| |
执行 ".sql" 文件
db.executescript(your_file_read_as_string)
| |
url_for
参考
作用
- 用来生成 url
签名
url_for(end_point, **values) -> str(url)
- 参数解说
- end_point -> 函数名
- **values –> 函数的参数 + url 参数查询参数(?name=key)
Flask logging
参考
- 使用 app.logger 输出 log
注意
关于 root_logger 问题
- 即,除掉在 app.logger 以外 模块的 logging 输出配置
解决方法
- 在创建 app 之前,先配置完成 logging
eg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29from logging.config import dictConfig dictConfig({ 'version': 1, 'formatters': {'default': { 'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s', }}, 'handlers': { 'rotate_file': { 'class': 'logging.handlers.RotatingFileHandler', 'filename': 'record.log', 'mode':'a', 'maxBytes': 1024*1024*10, 'backupCount': 10, 'encoding': 'utf-8', 'formatter': 'default' }, 'wsgi': { 'class': 'logging.StreamHandler', 'stream': 'ext://flask.logging.wsgi_errors_stream', 'formatter': 'default' }}, 'root': { 'level': 'INFO', 'handlers': ['wsgi', 'rotate_file'] } }) app = Flask(__name__)
Error handling 错误处理
监控插件
flask_monitoringdashboard
- 监控 api 调用情况
参考:GitHub - flask-dashboard/Flask-MonitoringDashboard: Automatically monitor the…
1 2 3 4 5from flask import Flask import flask_monitoringdashboard as dashboard app = Flask(__name__) dashboard.bind(app)调用 route
http://localhost/dashboard/overview
默认密码
- 用户:admin
- 密码:admin
async + flask
布署 deploy
文件下载(向客户端发送文件) download file
方法:
通过 flask.send_file() 函数直接转递文件路径
- 它会通过参数 file_path_or_io_object, 或者 download_name, 再或者 mime_type 来或者最终的 mime_type
- 如果没有找到 mime_type, 会选用 binary 格式发送文件,即
"application/octet-stream" 参数
as_attachment会设置 http headerContent-Disposition: attachment- 通知 Web 端,接收的是文件,不要直接展示
- 返回 Response 类型
特点:
- 下载时,会显示总大小,和进度条
例子
1 2 3 4 5 6@app.route("/file/whole/send_file", methods=["GET"]) def send_whole(): return send_file( "/mnt/e/Downloads/torch-2.1.1+cu118-cp310-cp310-linux_x86_64.whl", download_name="hello.whl", )
通过流 stream 实现流式下载
特点:
- 下载时,没有百分比和移动的进度条,没有总大小
- 通过 generator 实现
- 通过 generator + flask.stream_with_context 实现
flask.send_file() + 函数实现
- send_file 接收一个返回 IO 对象的函数
flask.send_file + IO 对象
eg:
1return send_file(open("/path/to/large_file", "rb"), download_name="example.data")
更多方法,参考:
stream 流式文件下载
自定义设置:
http header:
Content-Disposition: attachment; filename="example.pdf"- 通知客户端,下载数据是附件,不要直接展示
- filename="example.pdf", 给出文件的名称
方法:
直接使用
generator:- 这是一种基础用法
generator 返回的文本,当作文件内容
1 2 3 4 5 6@app.route('/large.csv') def generate_large_csv(): def generate(): for row in iter_all_rows(): yield f"{','.join(row)}\n" return generate(), {"Content-Type": "text/csv"}
使用
flask.stream_with_context特点:
- 允许在 generator 里面使用 flask.request 获取请求中的数据
- flask.stream_with_context 是一个装饰器
文章作者
上次更新 2024-07-16 (7f33ae8)