- Flask 框架
- 一、 简介
- 1、 框架介绍
- 2、 架构模式
- 3、 环境搭建
- 二、 第一个应用
- 三、 框架语法
- 1、 框架之配置
- 2、 框架之路由
- 2.1 添加路由
- 2.2 反向生成
- 2.3 路由系统
- 2.4 路由参数
- 2.5 添加装饰器
- 2.5.1 FBV
- 2.5.2 CBV
- 3、 请求与响应
- 4、 模板引擎
- 1、 变量
- 2、 继承
- 5、 session
- 5.1 源码分析
- 5.2 基本使用
- 5.3 过期时间
- 6、 请求拓展
- 7、 框架之闪现
- 8、 中间件
- 9、 蓝图
- 9.1 分区式架构
- 9.1.1 目录结构
- 9.1.2 代码实现
- 9.1.3 访问
- 9.2 功能式架构
- 9.2.1 目录结构
- 9.2.2 代码实现
- 9.1 分区式架构
- 10、 文件上传
- 四、 上下文管理
- 1、 Local对象
- 1.1
threading.local
- 1.2 基于函数
- 1.3 基于对象
- 1.1
- 2、 上下文管理阶段
- 3、 g
- 1、 Local对象
- 一、 简介
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标准库外,它不依赖于任何第三方模块。
Flask的架构模式-MTV
- 经典三层结构 :MVC模式
- M :Models ,模型层,负责数据库建模
- V :Views,视图层,用于处理用户显示的内容,如 :html
- C :Controller,控制器,处理与用户交互的部分内容。处理用户的请求并给出响应
- python常用:MTV模式
- M :Models ,模型层,负责数据库建模
- T :Templates ,模板层,用于处理用户显示的内容,如:html
- V :Views ,视图层,处理与用户交互的部分内容。处理用户的请求并给出响应
-
安装
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()
-
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
就拥有
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
还可以结合数据库和类等自己定义哦~~~
#!/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
中进行初始化
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.1threading.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
的东西
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对象被销毁