Flask路由和视图

Flask路由和视图

一、 路由系统

1. 路由系统基础

  • 路由装饰器

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

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

  • 转换器

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

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

    • 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 方法将视图函数注册到路由上。

    • 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 + 装饰器

  • 示例

    • 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 + 装饰器

  • 示例

    • 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返回一个完全配置好的视图函数,它可以直接注册到路由中。
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
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)

  • 11
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flask是基于Python的微型Web框架,它的路由系统非常灵活,可以让你轻松地设计应用程序的路由视图。下面是一个基本的Flask应用程序的路由视图的设计: 1. 导入Flask模块和必要的扩展: ```python from flask import Flask, render_template, request, redirect, url_for ``` 2. 创建一个Flask应用程序实例: ```python app = Flask(__name__) ``` 3. 定义路由视图: ```python # 首页路由视图 @app.route('/') def index(): return render_template('index.html') # 表单提交路由视图 @app.route('/submit', methods=['POST']) def submit(): name = request.form['name'] email = request.form['email'] return redirect(url_for('thanks', name=name, email=email)) # 感谢页面路由视图 @app.route('/thanks') def thanks(): name = request.args.get('name') email = request.args.get('email') return render_template('thanks.html', name=name, email=email) ``` 4. 运行应用程序: ```python if __name__ == '__main__': app.run() ``` 在上面的代码中,我们定义了三个路由:首页路由、表单提交路由和感谢页面路由。首页路由返回index.html模板,表单提交路由接收POST请求并从表单中获取数据,然后重定向到感谢页面路由,并将数据传递给感谢页面视图。感谢页面路由返回thanks.html模板,并将数据传递给模板渲染。 上述是一个简单的Flask应用程序的路由视图的设计,当然在实际的应用开发中,可能会涉及更多的路由视图,但是设计思路是一样的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值