Flask框架
创始人
2024-04-20 12:58:49

Flask

  • 一 前言
  • 二 快速使用
  • 三 内置配置变量
  • 四 配置文件的写法
  • 五 路由
  • 六 cbv写法
    • 6.1 快速使用
    • 6.2 cbv加装饰器
    • 6.3 as_view的执行流程
    • 6.4 as_view的name参数
    • 6.5 继承View写cbv
  • 七 模板语法
    • 7.1 渲染变量
    • 7.2 变量的循环
    • 7.3 逻辑判断

一 前言

Flask是一个基于Python开发并且依赖jinja2模板(模板语言)和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器。

“微”(micro) 并不表示你需要把整个 Web 应用塞进单个 Python 文件(虽然确实可以 ),也不意味着 Flask 在功能上有所欠缺。微框架中的“微”意味着 Flask 旨在保持核心简单而易于扩展。Flask 不会替你做出太多决策——比如使用何种数据库。而那些 Flask 所选择的——比如使用何种模板引擎——则很容易替换。除此之外的一切都由可由你掌握。如此,Flask 可以与您珠联璧合。

默认情况下,Flask 不包含数据库抽象层、表单验证,或是其它任何已有多种库可以胜任的功能。然而,Flask 支持用扩展来给应用添加这些功能,如同是 Flask 本身实现的一样。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。Flask 也许是“微小”的,但它已准备好在需求繁杂的生产环境中投入使用。

Flask官方文档: https://dormousehole.readthedocs.io/en/latest/

二 快速使用

安装

pip install Flask

使用

from flask import Flask  # 导入Flask类# 实例化产生Flask对象 有了__name__ Flask才知道在哪里找到模板和静态文件
app = Flask(__name__)  # 使用route装饰器来声明路由匹配成功执行的视图函数
@app.route('/')
def hello_word():return 'Hello World!'if __name__ == '__main__':app.run()

三 内置配置变量

在Flask类实例化app对象时将默认配置赋值给了app.config

Flask/_init_

#: The configuration dictionary as :class:`Config`.  This behaves
#: exactly like a regular dictionary but supports additional methods
#: to load a config from files.
self.config = self.make_config(instance_relative_config))

flask/app.py

default_config = ImmutableDict({"ENV": None,"DEBUG": None,"TESTING": False,"PROPAGATE_EXCEPTIONS": None,"PRESERVE_CONTEXT_ON_EXCEPTION": None,"SECRET_KEY": None,"PERMANENT_SESSION_LIFETIME": timedelta(days=31),"USE_X_SENDFILE": False,"SERVER_NAME": None,"APPLICATION_ROOT": "/","SESSION_COOKIE_NAME": "session","SESSION_COOKIE_DOMAIN": None,"SESSION_COOKIE_PATH": None,"SESSION_COOKIE_HTTPONLY": True,"SESSION_COOKIE_SECURE": False,"SESSION_COOKIE_SAMESITE": None,"SESSION_REFRESH_EACH_REQUEST": True,"MAX_CONTENT_LENGTH": None,"SEND_FILE_MAX_AGE_DEFAULT": None,"TRAP_BAD_REQUEST_ERRORS": None,"TRAP_HTTP_EXCEPTIONS": False,"EXPLAIN_TEMPLATE_LOADING": False,"PREFERRED_URL_SCHEME": "http","JSON_AS_ASCII": True,"JSON_SORT_KEYS": True,"JSONIFY_PRETTYPRINT_REGULAR": False,"JSONIFY_MIMETYPE": "application/json","TEMPLATES_AUTO_RELOAD": None,"MAX_COOKIE_SIZE": 4093,}

ENV:应用运行于什么环境。 Flask 和 扩展可以根据环境不同而行为不同,如打开或 关闭调试模式。 env 属性映射了这个配置键。本变量由 FLASK_ENV 环境变量设置。如果本变量是在代码中设置的话,可能出 现意外。在生产环境中不要使用 development 。

DEBUG:是否开启调试模式。使用 flask run 启动开发服务器时,遇到未能处理的 异常时会显示一个交互调试器,并且当代码变动后服务器会重启。 debug 属性映射了这个配置键。当 ENV 是 ‘development’ 时,本变量会启用,并且会被 FLASK_DEBUG 环境变量 重载。如果本变量是在代码中设置的话,可能会出现意外。在生产环境中不要开启调试模式。

TESTING:开启测试模式。异常会被广播而不是被应用的错误处理器处理。扩展可能也会为 了测试方便而改变它们的行为。你应当在自己的调试中开启本变量。

PROPAGATE_EXCEPTIONS:异常会重新引发而不是被应用的错误处理器处理。在没有设置本变量的情况下, 当 TESTING 或 DEBUG 开启时,本变量隐式地为真。

PRESERVE_CONTEXT_ON_EXCEPTION:当异常发生时,不要弹出请求情境。在没有设置该变量的情况下,如果 DEBUG 为真,则本变量为真。这样允许调试器错误请求数据。本变量通常不 需要直接设置。

SECRET_KEY:密钥用于会话 cookie 的安全签名,并可用于应用或者扩展的其他安全需求。 密钥应当是一个长的随机的 bytes 或者 str 。例如,复制下面的 输出到你的配置中:

import secretsprint(secrets.token_hex())
# cd607def747f7d3f0d15219cf3e89b6e5be13aafe91d163a676ed6d54bb09ecd

PERMANENT_SESSION_LIFETIME:如果 session.permanent 为真, cookie 的有效期为本变量设置的数字, 单位为秒。本变量可能是一个 datetime.timedelta 或者一个 int 。

USE_X_SENDFILE:当使用 Flask 提供文件服务时,设置 X-Sendfile 头部。有些网络服务器, 如 Apache ,识别这种头部,以利于更有效地提供数据服务。本变量只有使用这 种服务器时才有效。

SERVER_NAME:通知应用其所绑定的主机和端口。子域路由匹配需要本变量。

APPLICATION_ROOT:通知应用应用的根路径是什么。这个变量用于生成请求环境之外的 URL。

SESSION_COOKIE_NAME:会话 cookie 的名称。假如已存在同名 cookie ,本变量可改变。

SESSION_COOKIE_DOMAIN:认可会话 cookie 的域的匹配规则。如果本变量没有设置,那么 cookie 会被 SERVER_NAME 的所有子域认可。如果本变量设置为 False ,那么 cookie 域不会被设置。

SESSION_COOKIE_PATH:认可会话 cookie 的路径。如果没有设置本变量,那么路径为 APPLICATION_ROOT ,如果 APPLICATION_ROOT 也没有设置,那么会是 / 。

SESSION_COOKIE_HTTPONLY:为了安全,浏览器不会允许 JavaScript 操作标记为“ HTTP only ”的 cookie 。

SESSION_COOKIE_SECURE:如果 cookie 标记为“ secure ”,那么浏览器只会使用基于 HTTPS 的请求发 送 cookie 。应用必须使用 HTTPS 服务来启用本变量。

SESSION_COOKIE_SAMESITE:限制来自外部站点的请求如何发送 cookie 。可以被设置为 ‘Lax’ (推荐) 或者 ‘Strict’ 。

SESSION_REFRESH_EACH_REQUEST:当 session.permanent 为真时,控制是否每个响应都发送 cookie 。每次 都发送 cookie (缺省情况)可以有效地防止会话过期,但是会使用更多的带宽。 会持续会话不受影响。

MAX_CONTENT_LENGTH:在进来的请求数据中读取的最大字节数。如果本变量没有配置,并且请求没有指 定 CONTENT_LENGTH ,那么为了安全原因,不会读任何数据。

SEND_FILE_MAX_AGE_DEFAULT:当提供文件服务时,设置缓存控制最长存活期,以秒为单位。可以是一个 datetime.timedelta 或者一个 int 。在一个应用或者蓝图上 使用 get_send_file_max_age() 可以基于单个文件重载 本变量。

TRAP_BAD_REQUEST_ERRORS:尝试操作一个请求字典中不存在的键,如 args 和 form ,会返回一个 400 Bad Request error 页面。开启本变量,可以把这种错误作为一个未处理的 异常处理,这样就可以使用交互调试器了。本变量是一个特殊版本的 TRAP_HTTP_EXCEPTIONS 。如果没有设置,本变量会在调试模式下开启。

TRAP_HTTP_EXCEPTIONS:如果没有处理 HTTPException 类型异常的处理器,重新引发该异常用于被 交互调试器处理,而不是作为一个简单的错误响应来返回。

EXPLAIN_TEMPLATE_LOADING:记录模板文件如何载入的调试信息。使用本变量有助于查找为什么模板没有载入 或者载入了错误的模板的原因。

PREFERRED_URL_SCHEME:当不在请求情境内时使用些预案生成外部 URL 。

JSON_AS_ASCII:把对象序列化为 ASCII-encoded JSON 。如果禁用,那么 jsonify 返回 的 JSON 会包含 Unicode 字符。这样的话,在把 JSON 渲染到 JavaScript 时会有安全隐患。因此,通常应当开启这个变量。

JSON_SORT_KEYS:按字母排序 JSON 对象的键。这对于缓存是有用的,因为不管 Python 的哈希种 子是什么都能够保证数据以相同的方式序列化。为了以缓存为代价的性能提高可 以禁用它,虽然不推荐这样做。

JSONIFY_PRETTYPRINT_REGULAR:jsonify 响应会输出新行、空格和缩进以便于阅读。在调试模式下总是启用 的。

JSONIFY_MIMETYPE:jsonify 响应的媒体类型。

TEMPLATES_AUTO_RELOAD:当模板改变时重载它们。如果没有配置,在调试模式下会启用。

MAX_COOKIE_SIZE:当 cookie 头部大于本变量配置的字节数时发出警告。缺省值为 4093 。 更大的 cookie 会被浏览器悄悄地忽略。本变量设置为 0 时关闭警告。

四 配置文件的写法

1. 直接通过app对象添加配置

app = Flask(__name__)
print(app.config)
app.debug = True
app.secret_key = secrets.token_hex()
print(app.config)

在这里插入图片描述
2. 通过app.config配置

app = Flask(__name__)
print(app.config)
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = secrets.token_hex()
print(app.config)

3. 通过配置文件

settings.py

import secretsDEBUG = True
SECRET_KEY = secrets.token_hex()

app.py

app = Flask(__name__)
app.config.from_pyfile('settings.py')
print(app.config)

4. 通过类配置

setting_class.py

# 开发配置
class DevelopmentConfig:DEBUG = TrueSERVER_NAME = 'localhost'# 上线配置
class ProductionConfig:DEBUG = FalseSERVER_NAME = '192.168.1.11'

app.py

app.config.from_object('setting_class.ProductionConfig')
print(app.config)

5. 其他

app.config.from_envvar("环境变量名称")app.config.from_json("json文件名称")

五 路由

现代 web 应用都使用有意义的 URL ,这样有助于用户记忆,网页会更得到用户的青睐, 提高回头率。

使用 route() 装饰器来把函数绑定到 URL:

@app.route('/')
def index():return 'index page!'@app.route('/login')
def login():return 'login page!'

但是能做的不仅仅是这些!您可以动态变化 URL 的某些部分, 还可以为一个函数指定多个规则。

通过把 URL 的一部分标记为 就可以在 URL 中添加变量。标记的 部分会作为关键字参数传递给函数。通过使用 ,可以 选择性的加上一个转换器,为变量指定规则。请看下面的例子:

from flask import Flaskapp = Flask(__name__)@app.route('/user/')
def show_user_profile(username):# 显示该用户return f'User {username}'@app.route('/post/')
def show_post(post_id):# 显示id是一个数字return f'Post {post_id}'@app.route('/path/')
def show_subpath(subpath):# 显示path后的子路径return f'Subpath {subpath}'if __name__ == '__main__':app.run()

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
route参数

- methods:列表,规定请求的方式,如果列表中没有,该请求方式不被支持。
- endpoint:路由别名,如果不写,会以被装饰的函数名作为别名。

转换器类型:

类型描述
string接受任何不包含斜杠的字符串
int接受任意正整数
float接受正浮点数
path类似于string,但可以包含斜杠
uuid接受UUID字符串

路由源码

@app.route('/')
def hello_word():return 'Hello World!'

首先执行app对象(Flask的对象)的route()方法。

# rule是路由
def route(self, rule, **options):# 定义decorator函数def decorator(f):endpoint = options.pop("endpoint", None)self.add_url_rule(rule, endpoint, f, **options)return f# 返回decorator函数return decorator

route()函数执行完后,视图函数就相当于下面:

@decorator
def hello_word():return 'Hello World!'

接着执行decorator(hello_word)

# f是视图函数(hello_word)
def decorator(f):# 取出别名,不传就为Noneendpoint = options.pop("endpoint", None)# self是app对象# 接着调用app对象的add_url_rule方法self.add_url_rule(rule, endpoint, f, **options)# 返回视图函数return f

接着调用app对象的add_url_rule方法。

def add_url_rule(self, rule, endpoint=None, view_func=None, provide_automatic_options=None, **options):if endpoint is None:# 如果endpoint没传,就执行_endpoint_from_view_func方法# _endpoint_from_view_func返回视图函数的视图名# 所以不传endpoint参数,会以视图函数名作为endpointendpoint = _endpoint_from_view_func(view_func)  # type: ignoreoptions["endpoint"] = endpointmethods = options.pop("methods", None)# 如果没有传methods参数if methods is None:# 就从视图函数中反射methods属性,没有的话就为一个元组("GET",)methods = getattr(view_func, "methods", None) or ("GET",)# methods不能是字符串if isinstance(methods, str):raise TypeError("Allowed methods must be a list of strings, for"' example: @app.route(..., methods=["POST"])')# 这是一个集合生成式,保证集合里的元素不重复methods = {item.upper() for item in methods}# 添加必要的方法 也是一个集合required_methods = set(getattr(view_func, "required_methods", ()))# starting with Flask 0.8 the view_func object can disable and# force-enable the automatic options handling.if provide_automatic_options is None:provide_automatic_options = getattr(view_func, "provide_automatic_options", None)if provide_automatic_options is None:if "OPTIONS" not in methods:provide_automatic_options = Truerequired_methods.add("OPTIONS")else:provide_automatic_options = False# Add the required methods now.# | 表示集合求并集methods |= required_methods# self.url_rule_class是Flask类实例化时设置的# url_rule_class = Rule# rule就是Rule实例化的对象rule = self.url_rule_class(rule, methods=methods, **options)rule.provide_automatic_options = provide_automatic_options  # type: ignore# self.url_map也是在Falsk实例化时设置的# self.url_map = self.url_map_class()# url_map_class = Mapself.url_map.add(rule)if view_func is not None:# self.view_functions: t.Dict[str, t.Callable] = {} 是一个字典old_func = self.view_functions.get(endpoint)if old_func is not None and old_func != view_func:raise AssertionError("View function mapping is overwriting an existing"f" endpoint function: {endpoint}")# self.view_functions = {'别名':视图函数}self.view_functions[endpoint] = view_func

_endpoint_from_view_func(view_func)方法

def _endpoint_from_view_func(view_func: t.Callable) -> str:"""Internal helper that returns the default endpoint for a givenfunction.  This always is the function name."""# 断言视图函数不为空assert view_func is not None, "expected view func if endpoint is not provided."# 返回视图函数的函数名return view_func.__name__

总结:

路由注册其实就是调用了app对象的add_url_rule()方法,那么也可以不用装饰器的方式来注册路由。

app.add_url_rule('/user/', view_func=show_user_profile)

app.route和app.add_url_rule参数:

rule:URL规则
view_func:视图函数名称
defaults = None:默认值, 当URL中无参数,函数需要参数时,使用defaults = {'k': 'v'}
endpoint = None:名称
methods = None:允许的请求方式,如:["GET", "POST"]
strict_slashes:对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:重定向到指定地址@app.route('/index/', redirect_to='/home/')

使用装饰器完成登录认证

from flask import Flask, render_template, request, redirect, session, url_forapp = Flask(__name__)
app.debug = True
app.secret_key = 'sdfsdfsdfsdf'USERS = {1: {'name': '张三', 'age': 18, 'gender': '男', 'text': "道路千万条"},2: {'name': '李四', 'age': 28, 'gender': '男', 'text': "安全第一条"},3: {'name': '王五', 'age': 18, 'gender': '女', 'text': "行车不规范"},
}from functools import wrapsdef auth(func):# 这里如果不使用wraps装饰器,func视图函数就变成了inner# endpoint就会重复,程序就会报错# 这里不加就要在route函数中指定endpoint参数@wraps(func)def inner(*args, **kwargs):user = session.get('user_info')if user:res = func(*args, **kwargs)return resreturn redirect('/login')return inner@app.route('/detail/', methods=['GET'])
@auth
def detail(nid):info = USERS.get(nid)return render_template('detail.html', info=info)@app.route('/index', methods=['GET'])
@auth
def index():return render_template('index.html', user_dict=USERS)@app.route('/login', methods=['GET', 'POST'])
def login():if request.method == "GET":return render_template('login.html')else:# request.query_stringuser = request.form.get('user')pwd = request.form.get('pwd')if user == 'xuxiaoxu' and pwd == '123':session['user_info'] = userreturn redirect('/index')return render_template('login.html', error='用户名或密码错误')if __name__ == '__main__':app.run()

六 cbv写法

6.1 快速使用

from flask import Flask
from flask.views import MethodViewapp = Flask(__name__)class Test(MethodView):def get(self):return 'get'def post(self):return 'post'app.add_url_rule(rule='/test', view_func=Test.as_view(name='test'))if __name__ == '__main__':app.run()

6.2 cbv加装饰器

View/as_view

# cls是视图类
# 如果在视图类中配置了decorators属性
if cls.decorators:# 将as_view传入的name参数赋值给view函数的名字view.__name__ = nameview.__module__ = cls.__module__# 循环拿出每一个装饰器函数for decorator in cls.decorators:# 相当于		# @decorator # def view():#     passview = decorator(view)

6.3 as_view的执行流程

View/as_view

@classmethod
# 是一个类方法 
# cls是视图类,name必须传,不传会报错。
def as_view(cls, name, *class_args, **class_kwargs):# 定义view函数def view(*args, **kwargs):self = view.view_class(*class_args, **class_kwargs)  # type: ignorereturn current_app.ensure_sync(self.dispatch_request)(*args, **kwargs)# 如果有装饰器,在这里执行装饰器if cls.decorators:view.__name__ = nameview.__module__ = cls.__module__for decorator in cls.decorators:view = decorator(view)# 修改和增加view函数的属性view.__name__ = nameview.__doc__ = cls.__doc__view.__module__ = cls.__module__view.methods = cls.methods  # type: ignoreview.provide_automatic_options = cls.provide_automatic_options  # type: ignore# 返回view函数return view

请求匹配成功,会执行as_view中的view

def view(*args, **kwargs):# view.view_class是在执行as_view赋值给view函数的,是视图类# 所以self就是视图类的对象self = view.view_class(*class_args, **class_kwargs)  # type: ignore# 先执行self.dispatch_requestreturn current_app.ensure_sync(self.dispatch_request)(*args, **kwargs)

接着执行self.dispatch_request self是视图类的对象。

def dispatch_request(self, *args, **kwargs):# 从视图类对象中获取请求方式的视图函数内存地址meth = getattr(self, request.method.lower(), None)# If the request method is HEAD and we don't have a handler for it# retry with GET.if meth is None and request.method == "HEAD":meth = getattr(self, "get", None)assert meth is not None, f"Unimplemented method {request.method!r}"# 执行视图函数return current_app.ensure_sync(meth)(*args, **kwargs)

6.4 as_view的name参数

执行了as_view()函数,as_view的内层函数view的函数名就变成了name指定的值。

接着执行app.add_url_rule函数,如果没有指定endpoint参数,endpoint是view函数的函数名,就是as_view传的name参数。如果指定了endpoint参数,endpoint就是指定的endpoint参数值,和view的函数名就没有关系了。

6.5 继承View写cbv

from flask import Flask
from flask.views import Viewapp = Flask(__name__)class Test(View):def get(self):return 'get'app.add_url_rule('/test', view_func=Test.as_view(name='test'))if __name__ == '__main__':app.run()
def dispatch_request(self) -> ResponseReturnValue:"""Subclasses have to override this method to implement theactual view function code.  This method is called with allthe arguments from the URL rule."""raise NotImplementedError()

在执行self.dispatch_request是或抛出NotImplementedError异常,继承View写cbv需要自己定制dispatch_request方法。

七 模板语法

7.1 渲染变量



Title

用户列表

{% for k,v in user_dict.items() %}{% endfor %}
{{k}}{{v.name}}{{v['name']}}{{v.get('name')}}{k}}">查看详细

7.2 变量的循环



Title

用户列表

{% for k,v in user_dict.items() %}{% endfor %}
{{k}}{{v.name}}{{v['name']}}{{v.get('name')}}{k}}">查看详细

7.3 逻辑判断



Title

用户列表

{% if name %}

Hello {{ name }}!

{% else %}

Hello World!

{% endif %}

比django中多可以加括号,执行函数,传参数

# Markup等价django的mark_safe 
from flask import Flask,render_template,Markup,jsonify,make_response
app = Flask(__name__)def func1(arg):return Markup("" %(arg,))
@app.route('/')
def index():return render_template('index.html',ff = func1)if __name__ == '__main__':app.run()

相关内容

热门资讯

北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
阿西吧是什么意思 阿西吧相当于... 即使你没有受到过任何外语培训,你也懂四国语言。汉语:你好英语:Shit韩语:阿西吧(아,씨발! )日...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...