flask介绍
Flask是一个轻量级的可定制框架,使用Python语言编写,较其他同类型框架更为灵活、轻便、安全且容易上手。它可以很好地结合MVC模式进行开发,开发人员分工合作,小型团队在短时间内就可以完成功能丰富的中小型网站或Web服务的实现。另外,Flask还有很强的定制性,用户可以根据自己的需求来添加相应的功能,在保持核心功能简单的同时实现功能的丰富与扩展,其强大的插件库可以让用户实现个性化的网站定制,开发出功能强大的网站。
一. 快速使用
安装框架
pip install Flask
第一个"Hello World"
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World'
if __name__ == '__main__':
app.run(host='127.0.0.1',port=8000)
二. 配置文件写法
在flask类实例app对象时将默认配置存放到app.config中
Flask/_init_
self.config = self.make_config(instance_relative_config))
方式一:直接通过app添加配置
app = Flask(__name__)
print(app.config)
app.debug = True
app.secret_key = secrets.token_hex()
print(app.config)
方式二:通过app.config配置
app = Flask(__name__)
print(app.config)
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = secrets.token_hex()
print(app.config)
方式三:通过配置文件配置
'''settings.py'''
import secrets
DEBUG = True
SECRET_KEY = secrets.token_hex()
'''app.py'''
app = Flask(__name__)
app.config.from_pyfile('settings.py')
print(app.config)
方式四:通过类的方式配置
新建一个py文件定义配置类:setting_class.py
# 开发配置
class DevelopmentConfig:
DEBUG = True
SERVER_NAME = 'localhost'
# 上线配置
class ProductionConfig:
DEBUG = False
SERVER_NAME = '192.168.1.143'
app.py
app.config.from_object('setting_class.ProductionConfig')
print(app.config)
其他方式了解:
app.config.from_envvar("环境变量名称")
app.config.from_json("json文件名称")
三. 路由系统
在django中路由是单独的urls.py文件,在flask中路由是装饰器的形式把函数绑定到URL
- 使用route()装饰器方式
@app.route('/')
def index():
return 'index'
@app.route('/home')
def login():
return 'home'
- route()重要参数
-rule: 字符串的路径,使用转换器 <string:name> <name>
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter, # /xx/sss/
'int': IntegerConverter, # 数字
'float': FloatConverter, #小数
'uuid': UUIDConverter, #asdfas-asdfas-asdf
#常用:string,int,path
-methods: 列表,规定请求的方式,如果列表中没有,该请求方式不被支持
-endpoint:路由别名,如果不写,会以被装饰的函数名作为别名,django中叫name
- 路由源码执行流程
首先执行app对象的route()
方法我们先看route源码
def route(self, rule, **options):
# 定义decorator函数
def decorator(f):
# endpoint为路由别名,如果没有传这里就是None
endpoint = options.pop("endpoint", None)
# self是Flask的对象,app :rule路由, endpoint:别名,是None,其他的打散了传入了(methods..)
self.add_url_rule(rule, endpoint, f, **options)
return f
# 返回decorator函数
return decorator
执行完route后相当于函数变成了下面形式
@decorator
def index():
return 'index'
开始执行decorator(index)
# f是视图函数(index)
def decorator(f):
# 取出别名,不传就为None
endpoint = options.pop("endpoint", None)
# self是app对象
# 调用app对象的add_url_rule方法
self.add_url_rule(rule, endpoint, f, **options)
# 返回视图函数
return f
发现最后执行了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参数,会以视图函数名作为endpoint
endpoint = _endpoint_from_view_func(view_func) # type: ignore
options["endpoint"] = endpoint
methods = 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 = True
required_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 = Map
self.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
总结:看了路由的源码理解了其执行流程后我们得出路由注册其实就是调用了app对象的add_url_rule()
方法,那么我们也可以不使用装饰器来注册路由了直接自己来写
app.add_url_rule('/home', view_func=home, endpoint='home')
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/<int:nid>', redirect_to='/home/<nid>')
四. CBV写法
继承Methodview快速使用
from flask import Flask
from flask.views import MethodView
app = Flask(__name__)
class index(MethodView):
def get(self):
return 'get'
def post(self):
return 'post'
app.add_url_rule(rule='/index', view_func=Test.as_view(name='test'))
if __name__ == '__main__':
app.run()
给cbv加装饰器我们来看看是如何做的
def as_view(cls, name, *class_args, **class_kwargs ) :
....
# cls是视图类
# 如果在视图类中配置了decorators属性
if cls.decorators:
# 将as_view传入的name参数赋值给view函数的名字
view.__name__ = name
view.__module__ = cls.__module__
# 循环拿出每一个装饰器函数
for decorator in cls.decorators:
'''
# 装饰器原理:
@auth
def view():
本质是 view=auth(view)
'''
# 给view加装饰器----》给视图类中得方法加装饰器
view = decorator(view)
return 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: ignore
return current_app.ensure_sync(self.dispatch_request)(*args, **kwargs)
# 如果有装饰器,在这里执行装饰器
if cls.decorators:
view.__name__ = name
view.__module__ = cls.__module__
for decorator in cls.decorators:
view = decorator(view)
# 修改和增加view函数的属性
view.__name__ = name
view.__doc__ = cls.__doc__
view.__module__ = cls.__module__
view.methods = cls.methods # type: ignore
view.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_request
return 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)
五. 继承View写cbv
from flask import Flask
from flask.views import View
app = 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()
1.继承View的视图类,执行流程都是一样的
2.请求来了路由匹配成功,执行self.dispatch_request
3.self.dispatch_request没有实现,直接抛异常NotImplementedError()
4.必须充血 dispatch_request,自己写匹配规则,执行不同的方法