Flask基础内容
Flask 框架
一、 简介
1、 框架介绍
Flask是一个基于Python并且依赖于Jinja2模板引擎和Werkzeug WSGI 服务的一个微型框架
WSGI :Web Server Gateway Interface(WEB服务网关接口),定义了使用python编写的web app与web server之间接口格式
其他类型框架:
-
Django
:比较“重”的框架,同时也是最出名的Python框架。包含了web开发中常用的功能、组件的框架(ORM、Session、Form、Admin、分页、中间件、信号、缓存、ContenType....),Django是走大而全的方向,最出名的是其全自动化的管理后台:只需要使用起ORM,做简单的对象定义,它就能自动生成数据库结构、以及全功能的管理后台。 -
Tornado
:大特性就是异步非阻塞、原生支持WebSocket协议; -
Flask
: 一个轻量级的基于 Python 的 Web 框架 -
Bottle
:是一个简单高效的遵循WSGI的微型python Web框架。说微型,是因为它只有一个文件,除Python标准库外,它不依赖于任何第三方模块。
2、 架构模式
Flask的架构模式-MTV
- 经典三层结构 :MVC模式
- M :Models ,模型层,负责数据库建模
- V :Views,视图层,用于处理用户显示的内容,如 :html
- C :Controller,控制器,处理与用户交互的部分内容。处理用户的请求并给出响应
- python常用:MTV模式
- M :Models ,模型层,负责数据库建模
- T :Templates ,模板层,用于处理用户显示的内容,如:html
- V :Views ,视图层,处理与用户交互的部分内容。处理用户的请求并给出响应
3、 环境搭建
-
安装
pip install flask -
创建工作目录
mkdir first_dir # 创建工作目录 mkdir first_dir/static # 创建存放图片、css等不需要动态生成的静态文件 mkdir first_dir/templates # 创建存放响应文本的模板文件夹 touch first_dir/app.py # 主程序 -
在
app.py
中添加from flask import Flask app = Flask(__name__) @app.route('/') # Flask路由 def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run() # 运行网站 主程序会默认访问
templates
和static
文件夹,如果,存放web文件的文件夹名称不是这两个,那么要在实例化Flask
路由时,声明
flask中文网:https://flask.net.cn/
如果对一些语法有疑问,可以自行访问官网去查看
二、 第一个应用
主程序代码
from flask import (Flask, render_template , request, redirect, session) app = Flask(__name__) # 创建一个服务器 app.secret_key = "ashdjhfcasjcvbgjs" # 设置盐,对session信息进行加密,随机字符串 app.debug = True # 当文件保存时,flask服务就重启 USER_DICT = { "1": {"name": "李华", "age": 12}, "2": {"name": "李虎", "age": 13}, } # 假设这个为用户数据,其为从数据库中读取出来的 @app.route('/login', methods=["GET", "POST"]) def hello_world(): if request.method == "GET": # 如果请求方式为get请求 return render_template("login.html") # 对静态文件中的文件进行渲染 user = request.form.get("user") # 得到表单数据 pwd = request.form.get("pwd") if user == "kun" and pwd == "123": # 进行判断 # 用户信息放入session中,默认放入浏览器的cookie中 session["user_info"] = user return redirect("/index") # 进行重定向 return render_template("login.html", msg="账号或密码错误,登录失败") # 如果登录失败,将失败信息传入前端页面中 @app.route("/detail") def detail(): user_info = session.get("user_info") if not user_info: # 判断是否登录 return redirect("/login") # 如果没有登录,重定向到登录界面 uid = request.args.get("uid") # 获取传入的参数 info = USER_DICT.get(uid) # 获取人员信息 return render_template("detail.html", info=info) # 进行页面的渲染 @app.route("/index") def index(): user_info = session.get("user_info") if not user_info: # 如果没有用户信息,则返回登录页面 return redirect("/login") return render_template("index.html", user_dict=USER_DICT) @app.route("/loginout") def loginout(): del session["user_info"] # 删除cookies信息,进行注销操作 return redirect("/login") # 重定向到登录页面 @app.route("/") def red(): # 如果直接访问的话,默认跳转到登录界面 return redirect("/index") # 进行url的跳转 if __name__ == '__main__': app.run()
代码详情在:【https://github.com/liuzhongkun1/flask_/tree/main/firstFlask】
三、 框架语法
参考文档:【https://www.cnblogs.com/wupeiqi/asticles/7552008.html】
1、 框架之配置
# flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为: { 'DEBUG': get_debug_flag(default=False), # 是否开启Debug模式 'TESTING': False, # 是否开启测试模式 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': None, 'PERMANENT_SESSION_LIFETIME': timedelta(days=31), 'USE_X_SENDFILE': False, 'LOGGER_NAME': None, 'LOGGER_HANDLER_POLICY': 'always', 'SERVER_NAME': None, 'APPLICATION_ROOT': None, 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12), 'TRAP_BAD_REQUEST_ERRORS': False, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, 'JSONIFY_PRETTYPRINT_REGULAR': True, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None, } # 方式一: app.config['DEBUG'] = True # PS: 由于Config对象本质上是字典,所以还可以使用app.config.update(...) # 方式二: app.config.from_pyfile("python文件名称") """ 如: settings.py DEBUG = True app.config.from_pyfile("settings.py") """ app.config.from_envvar("环境变量名称") # 环境变量的值为python文件名称名称,内部调用from_pyfile方法 app.config.from_json("json文件名称") # JSON文件名称,必须是json格式,因为内部会执行json.loads app.config.from_mapping({'DEBUG':True}) # 字典格式 app.config.from_object("python类或类的路径") # 传入一个类 """ 如: app.config.from_object('pro_flask.settings.TestingConfig') settings.py class Config(object): DEBUG = False TESTING = False DATABASE_URI = 'sqlite://:memory:' class ProductionConfig(Config): DATABASE_URI = 'mysql://user@localhost/foo' class DevelopmentConfig(Config): DEBUG = True class TestingConfig(Config): TESTING = True PS: 从sys.path中已经存在路径开始写 PS: settings.py文件默认路径要放在程序root_path目录,如果instance_relative_config为True,则就是instance_path目录 """
具体的配置参数请通过官方文档获取
2、 框架之路由
2.1 添加路由
# 第一种 @app.route('/') def hello_world(): return 'Hello World!' # 第二种 def index(): return "index" app.add_url_rule("/index", None, index)
2.2 反向生成
from flask import Flask, url_for, redirect app = Flask(__name__) @app.route('/') def hello_world(): index_ = url_for("i") # 返回i对应的路由 print(index_) # 可以进行重定向 return redirect(index_) # 进行重定向 @app.route("/index/asdhjaskdg/sad", endpoint="i") # endpoint默认为函数名 def index(): return "index" if __name__ == '__main__': app.run()
2.3 路由系统
@app.route('/user/<username>')
:传递字符串类型的数据@app.route('/post/<int:post_id>')
:传递整型数据@app.route('/post/<float:post_id>')
:传递浮点类型数据@app.route('/post/<path:path>')
:传递路径@app.route('/<object:object>')
:传递自定义数据类型
from flask import Flask, url_for from werkzeug.routing import BaseConverter app = Flask(import_name=__name__) # 1.定义自己的转换器 class RegexConverter(BaseConverter): # map是固定的参数,将整个列表进行带入,regex是写入的参数,添加进新的转换器类型 def __init__(self, map, regex): # 调用父类的初始化方法 super(RegexConverter, self).__init__(map) self.regex = regex def to_python(self, value): # 路由匹配时,匹配成功后传递给视图函数中参数的值 return int(value) def to_url(self, value): # 使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数 val = super(RegexConverter, self).to_url(value) print(val) return val # 2.将自定义的转换器添加到flask的应用中 app.url_map.converters['regex'] = RegexConverter @app.route('/index/<regex("\d+"):nid>') def index(nid): print(url_for('index', nid='888')) return 'Index' if __name__ == '__main__': print(app.url_map) # 查看路由信息 app.run()
2.4 路由参数
-
rule
url
规则
-
voew_func
- 视图名称
-
defaults=None
- 默认值,当URL中无参数,函数需要参数时,使用
defaults={'k':'v'}
为函数提供参数
- 默认值,当URL中无参数,函数需要参数时,使用
-
endpoint=None
- 名称,用于反向生成URL,即:
url_for('名称')
- 名称,用于反向生成URL,即:
-
methods=None
- 允许请求的方式,默认为
GET
,可以是可迭代对象,如methods=["GET", "POST"]
- 允许请求的方式,默认为
-
strict_slashed=None
-
对
url
最后的\
是否有严格的要求,如@app.route('/index',strict_slashes=False) # 访问 http://www.xx.com/index/ 或 http://www.xx.com/index均可 @app.route('/index',strict_slashes=True) # 仅访问 http://www.xx.com/index
-
-
redirect_to=None
-
重定向到指定地址
from flask import Flask app = Flask(import_name=__name__) @app.route('/index', redirect_to='/home') # 或 redirect_to=home def func(): return "index" @app.route('/home') def home(): return "home" if __name__ == '__main__': app.run()
-
-
subdomain=None
-
匹配子域名
from flask import Flask app = Flask(import_name=__name__) @app.route("/", subdomain="<username>") # 注意,这里要使用域名才能使用 def main(username): print(username) return "hello" if __name__ == '__main__': app.run()
-
2.5 添加装饰器
2.5.1 FBV
from flask import Flask from functools import wraps def outer(func): @wraps(func) # 使得内层函数的函数名为传入的函数的函数名,反正重名 def inner(*args, **kwargs): print("装饰器运行") return func(*args, **kwargs) return inner app = Flask(import_name=__name__) @app.route('/') @outer # 这个装饰器可以进行登录验证 def main(): return "hello" if __name__ == '__main__': app.run()
如果要给框架另增加装饰器,一定要在路由装饰器的下面添加
2.5.2 CBV
from flask import Flask, views from functools import wraps def decorator(func): @wraps(func) def inner(*args, **kwargs): print("装饰器运行") return func(*args, **kwargs) return inner app = Flask(import_name=__name__) # CBV 一 class IndexView(views.View): methods = ["GET", "POST"] # 请求方式 decorators = [decorator, ] # 添加装饰器,可以添加多个 # dispatch_request这个名字是固定的 def dispatch_request(self): return "index" app.add_url_rule("/index_cbv", view_func=IndexView.as_view(name="index_c")) # 这里的name相当于endpoint # CBV 二 class IndexView2(views.MethodView): ''' 在views.MethodView中,相较于views.View,函数dispatch_request,已经在代码中写好了,反射getattr ''' methods = ["GET", "POST"] decorators = [decorator, ] def get(self): return "get" # get请求返回的值 def post(self): return "post" # post请求返回的值 app.add_url_rule("/index_cbv2", view_func=IndexView2.as_view(name="index_c2")) # 这里的name相当于endpoint if __name__ == '__main__': app.run()
3、 请求与响应
from flask import Flask, request, render_template, redirect, jsonify\ , make_response # 返回响应头 app = Flask(__name__) @app.route('/') def index(): """请求相关信息""" # request.method # request.args # request.form # request.values # request.cookies # request.headers # request.path # request.files # obj = request.files['the_file_name'] # obj.save('/var/www/uploads/' + secure_filename(f.filename)) """以上的是常用的""" # request.full_path # request.script_root # request.url # request.base_url # request.url_root # request.host_url # request.host """响应相关""" """ 响应头的使用 response = make_response(render_template('index.html')) # 创建响应数据 print(type(response)) response.delete_cookie('key') # 删除cookie response.set_cookie('key', 'value') # 设置cookie response.headers['X-Something'] = 'A value' return response """ return "hello world" # 可以使用json.dumps({}) / jsonify({}) 返回JSON数据 # return render_template("index.html", n1=123) # 渲染静态文件,第二个参数可以是字典解包,或者等号传参,传递给前端页面 # return redirect("/index") # 重定向 if __name__ == '__main__': app.run()
4、 模板引擎
1、 变量
index.html
里面的内容
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>index</title> </head> <body> <h5>{{ k1 }}</h5> <!--可以接收字符串--> <h4>{{ k2[0] }} {{ k2[1] }}</h4> <!--可以接收可迭代对象,和字典等--> <h5>匿名函数运行的结果为:{{ k3("pwd") | safe }}</h5> <!--接收函数对象 |safe 表示该取消对文本的转义,可以渲染出来--> </body> </html>
app.py
里面的内容
from flask import Flask, render_template, Markup app = Flask(__name__) def input_(value): # 生成input标签 return Markup("<input type='text' value='%s'/>" % value) # Markup 的功能和|safe一样,使得html内容可以渲染出来,关闭对文本信息的转义,其为过滤器 @app.route('/') def index(): context = { "k1": 123, "k2": [11, 12, 13], "k3": lambda x: input_(x), } return render_template("index.html", **context) if __name__ == '__main__': app.run()
2、 继承
parent.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>父类</title> </head> <body> <div>头部</div> <div> {% block content %} <!--里面放入子类继承的内容--> {% endblock %} </div> <div>尾部</div> </body> </html>
son.html
{% extends "parent.html" %} <!--继承父类文件--> {% block content %} <!--放入要放入的内容,如果放在外部,则不会被渲染--> <h5>{{ k1 }}</h5> <h4>{{ k2[0] }} {{ k2[1] }}</h4> <h5>匿名函数运行的结果为:{{ k3("pwd") | safe }}</h5> {% endblock %}
支持多继承
更多的模板编程语法:【https://blog.csdn.net/u013075024/article/details/121870876】
5、 session
除请求对象之外,还有一个 session 对象。它允许你在不同请求间存储特定用户的信息。它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名要使用会话,你需要设置一个密钥
字典中拥有的方法,session
就拥有
5.1 源码分析
app.run()
运行前,会运行app.__call__
,然后返回app.wsgi_app(environ, start_response)
,然后通过ctx = Request_Context(app, environ)
创建一个对象,在这时,使用ctx.push()
方法,传入session
,当请求结束后,将session
序列化以后,返回到cookie
里面,并且调用ctx.auto_pop(error)
方法,删除ctx
"""当发起请求时""" session = session_interface.open_session(self.app, self.request) # 给session赋值 session = SecureCookieSessionInterface().open_session(self.app, self.request) session = CallbackDict() # 其为一个自定义字典对象 ---------------------------------------------------------------- """当请求结束时""" self.session_interface.save_session(self, ctx.session, response) # 将session保存到response中 SecureCookieSessionInterface().save_session(self, ctx.session, response) # 最后在SecureCookieSessionInterface()中 val = self.get_signing_serializer(app).dumps(dict(session)) # 将session序列化 response.set_cookie( name, # self.get_cookie_name(app) val, # type: ignore expires=expires, # self.get_expiration_time(app, session) httponly=httponly, # self.get_cookie_httponly(app) domain=domain, # self.get_cookie_domain(app) path=path, # self.get_cookie_path(app) secure=secure, # self.get_cookie_secure(app) samesite=samesite, # self.get_cookie_samesite(app) )
5.2 基本使用
from flask import Flask, session, escape import os app = Flask(__name__) app.secret_key = os.urandom(32) # 设置一个密钥,随机的32位字符串 @app.route('/login/<username>') def index(username): session["name"] = username # __setitem__ 设置session return "index %s" % escape(username) # 使用escape对信息进行转义处理,防止恶意攻击,使用Markup可以进行反转义 @app.route('/verify') def main(): a = session.get("name") # __getitem__ 一般使用get方法获取值,因为不存在的话不会报错 print(a) return "main" @app.route("/delete") def delete(): session.pop("name", None) # 删除指定的键 return "删除成功" @app.route("/clear") def clear(): session.clear() # 清空session信息 return "清空成功" if __name__ == '__main__': app.run()
5.3 过期时间
如果没有指定session
的过期时间,那么默认是浏览器关闭后就自动结束。session.permanent = True
在flask
下则可以将有效期延长至一个月。下面有方法可以配置具体多少天的有效期:
- 如果没有指定
session
的过期时间,那么默认是浏览器关闭后就自动结束 - 如果设置了
session
的permanent
属性为True
,那么过期时间是31天 - 可以通过给
app.config
设置PERMANENT_SESSION_LIFETIME
来更改过期时间,这个值的数据类型是datetime.timedelay
类型
from flask import Flask import os from datetime import timedelta app = Flask(__name__) app.secret_key = os.urandom(32) # 设置一个密钥,随机的32位字符串 app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7) # 配置7天有效
session
还可以结合数据库和类等自己定义哦~~~
6、 请求拓展
#!/usr/bin/env python # -*- coding:utf-8 -*- from flask import Flask, Request, render_template app = Flask(__name__, template_folder='templates') app.debug = True @app.before_first_request # 只会执行一次,第一个访问者执行此函数 def before_first_request1(): print('before_first_request1') @app.before_request def before_request1(): Request.nnn = 123 print('before_request1') @app.before_request # 在请求发送前 def before_request2(): print('before_request2') @app.after_request # 在请求发送后的响应,注意,必须要接收响应,并且返回响应 def after_request1(response): print('before_request1', response) return response @app.errorhandler(404) # 使用abort(404)抛出异常 # 异常处理,如果异常代码是404的话 def page_not_found(error): return 'This page does not exist', 404 @app.template_global() # 创建函数为Flask全局函数 def sb(a1, a2): return a1 + a2 @app.template_filter('md') # 定义一个以模板过滤器作为装饰器的函数,函数传入参数txt,即将内容转换为md类型的文件 # 如 {{ "# title" | md }} # 将文本信息传入过滤器中,转换为md文本 def markdown_to_html(txt): from markdown import markdown return markdown(txt) @app.route('/') def hello_world(): return render_template('index.html') if __name__ == '__main__': app.run()
那装饰器函数可以添加多个,执行顺序是从上到下
7、 框架之闪现
#!/usr/bin/env python # -*- coding:utf-8 -*- import os from flask import Flask, flash, get_flashed_messages # 其为基于session来实现的 app = Flask(__name__, template_folder='templates') app.debug = True app.secret_key = os.urandom(32) @app.route('/') def login(): flash("hello", category="x1") # x1给数据分类,也可以不分类 return "你好呀" @app.route('/get_flash') def get_flash(): data = get_flashed_messages(category_filter=["x1"]) # 只拿想x1类型的数据,该数据只能访问一次,访问后就被删除 return str(data) if __name__ == '__main__': app.run()
闪现的数据只能访问一次,访问一次后就被删除了
8、 中间件
#!/usr/bin/env python # -*- coding:utf-8 -*- import os from flask import Flask app = Flask(__name__, template_folder='templates') app.debug = True app.secret_key = os.urandom(32) @app.route('/') def login(): return "你好呀" class MiddleWare: def __init__(self, old_wsgi_app): self.old_wsgi_app = old_wsgi_app def __call__(self, environ, start_response): """ 每次用户请求到来时 :param environ: A WSGI environment 原生的http请求 :param start_response: A callable accepting a status code, a list of headers, and an optional exception context to start the response :return: wsgi_app """ print("执行前的操作") obj = self.old_wsgi_app(environ, start_response) print("执行后的操作") return obj if __name__ == '__main__': app.wsgi_app = MiddleWare(app.wsgi_app) app.run() """ 1. 执行app.__call__ 2. 再调用app.wsgi_app方法 其相当于装饰器的使用方法 """
闪现和中间件的使用场景不多
9、 蓝图
9.1 分区式架构
9.1.1 目录结构
首先我们创建一个目录结构
blue-example/ ├── app.py ├── __init__.py ├── admin │ ├── __init__.py | ├── static | ├── templates │ └── views.py └── user ├── __init__.py ├── static ├── templates └── views.py
实现用户登录和管理员登录,同时将业务分离
9.1.2 代码实现
在app.py
中
#!/usr/bin/env python # -*- coding:utf-8 -*- from flask import Flask from admin import admin from web import web app = Flask(__name__) app.debug = True app.register_blueprint(admin, url_prefix='/admin') app.register_blueprint(web, url_prefix="/web") if __name__ == '__main__': app.run()
url_prefix
:在蓝图上注册的路由URL自动被加上了这个前缀,这个可以保证在多个蓝图中使用相同的URL规则而不会最终引起冲突,只要在注册蓝图时将不同的蓝图挂接到不同的自路径即可。默认为/
在admin
文件夹中
__init__.py
中写入
#!/usr/bin/env python # -*- coding:utf-8 -*- from flask import Blueprint admin = Blueprint( 'admin', __name__, template_folder='templates', static_folder='static' ) from . import views # 将视图中的代码导入init模块中
view.py
中写入
#!/usr/bin/env python # -*- coding:utf-8 -*- from . import admin # 从init文件中导入admin蓝图 @admin.route('/index') # 创建路由 def index(): return 'Admin.Index'
在user
文件夹中写入的内容和admin
文件夹的类似
__init__.py
文件创建蓝图对象
view.py
实现路由,在导入__init__.py
中进行初始化
9.1.3 访问
127.0.0.1:5000/admin/index 访问的是admin文件夹里面的路由 127.0.0.1:5000/user/index 访问的是user文件夹里面的路由
9.2 功能式架构
9.2.1 目录结构
功能式架构的目录结构
blue-example/ ├── __init__.py ├── app.py ├── statics └── templates ├── user └── admin └── views ├── __init__.py ├── user.py └── admin.py
9.2.2 代码实现
在app.py
中
from flask import Flask app = Flask(__name__, template_folder='templates', static_folder='statics', static_url_path='/static') from views.admin import admin from views.user import user app.register_blueprint(admin, url_prefix='/admin') app.register_blueprint(user, url_prefix='/user') if __name__ == '__main__': app.run()
static_url_path
:端访问资源文件的前缀目录。默认是/static
,就是前端必须这样访问:src="/static/img/mylogo.jpg" />
我们改成 ' ',就可以这样访问了:<img src="/img/mylogo.jpg" />
。就达到前端从根目录访问的目的了同时,使用
url_for("static", filename="xxx.png")
时,要添加static_url_path="/static"
在admin.py
中
#!/usr/bin/env python # -*- coding:utf-8 -*- from flask import Blueprint from flask import render_template admin = Blueprint('admin', __name__) @admin.route('/login.html', methods=['GET', "POST"]) def login(): return render_template('login.html')
访问方式是:
127.0.0.1:5000/admin/login.html
在user.py
中的内容类似
两种架构并没有好坏之分,可以根据具体的情况来确定方案
10、 文件上传
在 Flask 中处理文件上传非常简单。它需要一个 HTML 表单,其 enctype
属性设置为“multipart/form-data”
,将文件发布到 URL
URL 处理程序从 request.files[]
对象中提取文件,并将其保存到所需的位置
同时,要给app
添加的配置
app.config['UPLOAD_FOLDER'] # 定义上传文件夹的路径 app.config['MAX_CONTENT_LENGTH'] # 指定要上传的文件的最大大小(以字节为单位)
如,一个上传文件的文件,upload.html
<html> <head> <title>File Upload</title> </head> <body> <form action="http://localhost:5000/uploader" method="POST" enctype="multipart/form-data"> <input type="file" name="file" /> <input type="submit" value="提交" /> </form> </body> </html>
在主程序中
from flask import Flask, render_template, request, url_for, redirect from werkzeug.utils import secure_filename # 过滤文件的名称,反正被攻击 import os app = Flask(__name__) app.config['UPLOAD_FOLDER'] = 'upload/' # 设置上传的文件目录 @app.route('/upload') def upload_file(): return render_template('upload.html') # 对文本信息进行渲染 @app.route('/uploader', methods=['GET', 'POST']) def uploader(): if request.method == 'POST': # 如果为post请求,则为发送文件 f = request.files['file'] # 获取传递过来的文件 print(f.filename) # 打印文件 f.save(os.path.join(app.config['UPLOAD_FOLDER'], secure_filename(f.filename))) return 'file uploaded successfully' else: return redirect(url_for("upload_file")) # 对GET请求进行重定向 if __name__ == '__main__': app.run(debug=True)
四、 上下文管理
详细介绍:【https://www.cnblogs.com/zhaopanpan/articles/9457343.html】
1、 Local对象
1.1 threading.local
from threading import local, Thread from time import sleep local_ = local() def task(i): # 字典,键为线程的ident作为唯一标识 local_.val = i sleep(2) print(local_.val) for i in range(10): t = Thread(target=task, args=(i,)) t.start()
作用:
- 为每一个线程开辟一块空间进行数据存储
问题:
- 我们自己通过字典创建一个类似于
threading.local
的东西
1.2 基于函数
from threading import Thread, get_ident from time import sleep storage = {} def set(k, v): ident = get_ident() if ident in storage.keys(): storage[ident][k] = v # 给ident对应的值设置为{k, v} else: storage[ident] = {k: v} # 创建ident键,并将值设置为{k, v} def get(k): ident = get_ident() try: # 如果键不存在,则返回None val = storage[ident][k] return val except KeyError: return None def task(arg): set("val", arg) sleep(2) print(get("val")) for i in range(10): t = Thread(target=task, args=(i,)) t.start()
1.3 基于对象
from threading import Thread from time import sleep try: from greenlet import getcurrent as get_ident # 获取当前协程的唯一标识 except ImportError: from threading import get_ident # 如果无法使用协程,那么继续使用线程的唯一标识 # 使其为每一个协程(线程)开辟一块空间进行数据存储 class MyLocal: def __init__(self): # data存储一个字典 # self.data = {} # 会触发__setattr__方法 object.__setattr__(self, "data", {}) # 调用父类中的__setattr__方法,避免重复调用 def __getattr__(self, k): # 实现local.key方法 ident = get_ident() try: # 如果键不存在,则返回None val = self.data[ident][k] return val except KeyError: return None def __setattr__(self, key, value): # 实现local.key = value ident = get_ident() if ident in self.data: self.data[ident][key] = value else: self.data[ident] = {key: value} local = MyLocal() def task(arg): local.val = arg # __setattr__ sleep(2) print(local.val) # __getattr__ for i in range(10): t = Thread(target=task, args=(i,)) t.start() print(local.data)
2、 上下文管理阶段

1. 第一阶段:将ctx(request, session)放到Local对象里面 - ctx = RequestContext() 2. 第二阶段:视图函数导入: - request/session 3. 第三阶段:请求处理完毕 - 获取session并保存到cookie - 将ctx删除
3、 g
与请求上下文类似,当请求进来时,先实例化一个AppContext
对象app_ctx
,在实例化的过程中,提供了两个有用的属性,一个是app
,一个是g
。self.app
就是传入的全局的app
对象,self.g
是一个全局的存储值的对象。接着将这个app_ctx
存放到LocalStack()
像数据库配置这样重要的信息挂载在app对象上,一些用户相关的数据,就可以挂载在g对象上,这样就不需要在函数里一层层传递
from flask import Flask, g app = Flask(__name__) @app.before_request def auth_demo(): g.val = 123 @app.route('/') def index(): print(g.val) return "Hello World" if __name__ == '__main__': app.run(debug=True)
g对象的生命周期和当前的请求对应,当请求结束时,g对象被销毁
4、 总结
请求刚进来: RequestContext(request, session), AppContext(app, g) -> LocalStack -> Local 视图处理: LocalProxy -> 偏函数 -> LocalStack -> Local 请求结束: save_session -> LocalStack.pop()
Local
作用:
- 用于保存数据
- 请求上下文对象
app
上下文对象- app上下文对象
- 并且可以做到线程间的数据隔离
LocalStack
作用:
- 将
Local
中保存的数据维护成一个栈
本文来自博客园,作者:Kenny_LZK,转载请注明原文链接:https://www.cnblogs.com/liuzhongkun/p/16273127.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?