Flask路由和视图

Flask路由和视图

一、 路由系统

1. 路由系统基础

  • 路由装饰器

    • Flask使用装饰器@app.route来将URL规则绑定到视图函数上。

    • 装饰器可以指定路径规则(rule)、请求方法(methods)、以及别名(endpoint)等。

  • 转换器

    • Flask默认支持多种路径转换器,如intfloatstring等,用于将URL中的变量部分转换为不同的数据类型。

    • 转换器使得URL可以动态匹配,并允许开发者定义复杂的URL规则。

    • python 复制代码
      DEFAULT_CONVERTERS = {
       'default':          UnicodeConverter,
       'string':           UnicodeConverter,
       'any':              AnyConverter,
       'path':             PathConverter,
       'int':              IntegerConverter,
       'float':            FloatConverter,
       'uuid':             UUIDConverter,
      }

2. 执行流程分析

  1. 路由装饰器

    • @app.route 是一个装饰器,使用它来将一个函数绑定到一个URL规则上。

    • 当装饰器被触发时,它实际上执行了 index = decorator(index),其中 decoratorroute 方法返回的内部函数。

    • decorator 函数是 route 方法的内部函数,它接收视图函数 f 作为参数,并使用 add_url_rule 方法将视图函数注册到路由上。

    python 复制代码
    def route(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]:
        def decorator(f: T_route) -> T_route:
            endpoint = options.pop("endpoint", None)
            self.add_url_rule(rule, endpoint, f, **options)
            return f
        return decorator
  2. add_url_rule参数

    • rule:定义URL规则。
    • view_func:视图函数。
    • defaults:默认值,用于URL中没有参数但视图函数需要参数的情况。
    • endpoint:用于反向生成URL的名称。
    • methods:允许的HTTP请求方法。
    • strict_slashes:是否严格要求URL末尾的斜杠。
    • redirect_to:重定向到指定地址。
    • subdomain:子域名访问。

3. Endpoint

  1. 默认Endpoint生成
    • 如果在装饰器中没有指定endpoint,Flask会默认使用视图函数的名称作为endpoint
    • 如果存在多个路由使用了相同的视图函数,但没有指定不同的endpoint,会导致endpoint冲突。
  2. 避免Endpoint冲突
    • 可以在装饰器中明确指定endpoint参数,以避免冲突。
    • 使用自定义装饰器,通过包装视图函数来确保每个路由都有唯一的endpoint

二、 视图函数

1. FBV + 装饰器

  • 示例

    python 复制代码
    from functools import wraps
    from flask import Flask
    
    app = Flask(__name__)
    
    def outer(func):
        @wraps(func)
        def inner(*args, **kwargs):
            print('走了装饰器')
            return func(*args, **kwargs)
        return inner
    
    @app.route('/')
    @outer
    def index():
        return '根路径'
    
    @app.route('/home')
    @outer
    def home():
        return '主页'
    
    if __name__ == '__main__':
        app.run()
  • 说明:为什么在FBV中使用装饰器时,需要使用@wraps(func)或指定endpoint

    • 使用 @wraps(func)
      • 保持被装饰函数的__name____doc__属性,这样当查看inner函数时,它看起来就像是原始的func函数。
      • 这对于调试和文档来说非常重要,因为它们依赖于函数的名称和文档字符串。
    • 指定 endpoint
      • 通过指定endpoint,可以确保即使装饰器改变了函数的元信息,也可以通过端点名称来引用这个视图函数。

2. CBV + 装饰器

  • 示例

    python 复制代码
    from functools import wraps
    from flask import Flask
    from flask.views import MethodView
    
    app = Flask(__name__)
    
    def outer1(func):
        @wraps(func)
        def inner(*args, **kwargs):
            print('走了装饰器1')
            return func(*args, **kwargs)
        return inner
    
    def outer2(func):
        @wraps(func)
        def inner(*args, **kwargs):
            print('走了装饰器2')
            return func(*args, **kwargs)
        return inner
    
    
    class Home(MethodView):
        decorators = [outer1, outer2]
        methods = ['GET', "POST"]
    
        def get(self):
            return "GET"
    
        def post(self):
            return "POST"
    
    app.add_url_rule('/home', endpoint='home', view_func=Home.as_view('home'))
  • 说明

    • decorators = [装饰器1,]
      • 如果有多个可以直接 逗号隔开
    • methods = ['GET', "POST"]
      • 可以控制整个视图函数的请求方式

3. CBV源码解析

as_view 方法关键步骤:

  1. 检查 init_every_request 属性
    • 如果init_every_requestTrue,则每次请求都会创建类的新实例。
    • 如果为False,则类在第一次调用as_view时被实例化,并且这个实例在后续请求中被重用。
  2. 定义视图函数 view
    • 视图函数内部调用self.dispatch_request来处理请求。
  3. 支持异步操作
    • 使用current_app.ensure_sync确保视图函数和dispatch_request是同步的,即使它们可能是异步的。
  4. 应用装饰器
    • 如果类定义了decorators属性,这些装饰器会被应用到视图函数上。
    • 每个装饰器通过接收视图函数作为参数并返回一个新的函数来包装视图函数。
  5. 设置视图函数的元信息
    • 视图函数的__name____module____doc__属性被设置为类的对应属性,以便于调试和文档。
  6. 返回视图函数
    • 最终,as_view返回一个完全配置好的视图函数,它可以直接注册到路由中。
python 复制代码
def as_view(cls, name, *class_args, **class_kwargs):
 # 如果init_every_request为True,则每次请求都会创建类的新实例。
 # 如果为False,则类在第一次调用as_view时被实例化,并且这个实例在后续请求中被重用。
 if cls.init_every_request:
     def view(**kwargs):
         self = view.view_class(*class_args, **class_kwargs)
         # 支持异步操作
         return current_app.ensure_sync(self.dispatch_request)(**kwargs)

 else:
     self = cls(*class_args, **class_kwargs)
     def view(**kwargs):
         # 支持异步操作
         return current_app.ensure_sync(self.dispatch_request)(**kwargs)
 # 应用装饰器
 if cls.decorators:
     view.__name__ = name
     view.__module__ = cls.__module__
     for decorator in cls.decorators:
         view = decorator(view)
 # 设置视图函数的元信息        
 view.__name__ = name
 view.__doc__ = cls.__doc__
 view.__module__ = cls.__module__
 view.methods = cls.methods
 view.provide_automatic_options = cls.provide_automatic_options
 return view

dispatch_request 方法

  1. 获取请求方法
    • 通过request.method.lower()获取请求方法的字符串表示,并将其转换为小写。
  2. 查找对应的方法
    • 尝试获取与请求方法同名的类方法(例如,如果请求是GET,则查找get方法)。
    • 如果请求是HEAD,但没有对应的处理方法,它会尝试使用GET方法。
  3. 执行方法
    • 如果找到了对应的方法,它会使用current_app.ensure_sync来确保该方法同步执行,并传入请求的参数。
  4. 抛出异常
    • 如果没有找到对应的方法,dispatch_request会抛出NotImplementedError
PYTHON 复制代码
def dispatch_request(self, **kwargs):
 # 使用getattr函数获取请求方法
 meth = getattr(self, request.method.lower(), None)

 # 如果请求方法未找到,且请求方法为"HEAD",则使用getattr函数获取"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)(**kwargs)
相关推荐
hummhumm12 分钟前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
杜小满16 分钟前
周志华深度森林deep forest(deep-forest)最新可安装教程,仅需在pycharm中完成,超简单安装教程
python·随机森林·pycharm·集成学习
J老熊22 分钟前
JavaFX:简介、使用场景、常见问题及对比其他框架分析
java·开发语言·后端·面试·系统架构·软件工程
AuroraI'ncoding28 分钟前
时间请求参数、响应
java·后端·spring
好奇的菜鸟40 分钟前
Go语言中的引用类型:指针与传递机制
开发语言·后端·golang
Alive~o.01 小时前
Go语言进阶&依赖管理
开发语言·后端·golang
许苑向上1 小时前
Dubbo集成SpringBoot实现远程服务调用
spring boot·后端·dubbo
databook1 小时前
『玩转Streamlit』--布局与容器组件
python·机器学习·数据分析
郑祎亦2 小时前
Spring Boot 项目 myblog 整理
spring boot·后端·java-ee·maven·mybatis
nuclear20112 小时前
使用Python 在Excel中创建和取消数据分组 - 详解
python·excel数据分组·创建excel分组·excel分类汇总·excel嵌套分组·excel大纲级别·取消excel分组