zoukankan      html  css  js  c++  java
  • Flask高级处理

    1. 请求钩子
      请求钩子可以对请求的各阶段进行监听, 方便开发者 针对请求完成一些统一的处理, 以便减少重复代码, 作用类比Django中的中间件

      开发中中主要会用到以下四种请求钩子:
        before_request: 每次执行视图函数之前; 对请求进行一些准备处理; 如果在函数中返回了一个响应,视图函数将不再被调用
        after_request: 没有异常,每次执行视图函数之后(已经包装为响应对象)调用; 在此函数中可以对象应值再返回之前做最后一步修改处理
        before_first_request(不常用): 只在第一次访问该应用时执行,可以进行web应用初始化处理
        teardown_request: 每次执行视图函数之后调用;无论是否出现异常都会执行, 一般用于请求收尾;接受一个参数:错误信息,如果有相关错误抛出
    2. 蓝图(重点)
      蓝图的作用: 实现Flask项目 模块化
      项目模块化主要是 将业务以功能模块进行划分, 每个功能模块对应一个包, 用于存放和其有关的视图/工具/模型文件等, 如homeuser
      对于大型项目, 一般 每个功能模块对应创建一个蓝图, 由多个蓝图代替应用来分别管理各模块的视图
      大型项目目录结构示意图:
      --------- project # 工程目录
        |------ main.py # 启动文件
        |------ user  # 用户模块
        |  |--- __init__.py  # 包的初始化文件, 此处创建管理用户模块的蓝图对象
        |  |--- views.py  # 视图文件
        |  |--- ...
        |
        |------ home # 首页模块
        |  |--- __init__.py  # 包的初始化文件, 此处创建管理首页模块的蓝图对象
        |  |--- views.py  # 视图文件
        |  |--- ...
        |...

      蓝图的基本使用:

      1).创建蓝图对象 (创建home模块包,在里面的__init__.py文件里添加代码)

      from flask import Blueprint
      home_blue = Blueprint("home_b", __name__, url_prefix="/home")
      # 4. 让视图文件和程序建立关联(一旦遇到导包错误,解决办法:查看并调整代码顺序)
      from . import views


      2). 使用蓝图对象来定义路由 (home下视图文件views.py里)

      from home import home_blue
      @home_blue.route("/demo")
      def demo():
      return "demo"

      3).注册蓝图对象(在工程目录下的主成序启动文件的main.py里)

      from flask import Flask

      app = Flask(__name__)

      # 3. 注册蓝图对象
      from project.home import home_blue
      app.register_blueprint(home_blue)

      if __name__ == '__main__':
      # print(app.url_map)
      print(app.view_functions)
      app.run(debug=True)


      4).让视图文件和程序建立关联(这一步可能遇到导包错误,解决办法: 查看并调整代码的执行顺序)

        这一步是在home包下的__init__.py文件下操作

    3. 上下文(context)
      上下文:是一个 数据容器,用于保存Flask的各相关数据
      上下文分类: 请求上下文 和 应用上下文
      共同特点: 上下文中数据有使用范围 [请求开始:请求结束]

      请求上下文: 记录一些和请求有关的数据 request, session 等
      应用上下文: 记录一些和应用有关的数据 g current_app

      g: flask给开发者预留的一个容器,用于存储一些自定义数据 特点: 每次请求,g记录的数据会重置
      g的应用场景: 1> 在视图函数 和 钩子函数之间传递数据 2> 函数嵌套调用是, 可以使用g代替参数传递数据

      current_app: 会自定引用创建的flask应用,想要在项目的其他文件中使用app时,应该使用current_app,
      这样可以减少循环导入的错误

       

       

        

    4. 综合认证: 统一处理 ; 访问限制
      1.统一处理:
      需求: 获取用户身份
      分析: 除了静态资源, 基本所有视图都需要获取用户身份, 每个视图单独获取出现大量的代码冗余
      解决办法: 设置 请求钩子, 并通过 g变量 将数据传递给视图函数
      代码:
      ===================================================================================================================
      from flask import Flask, session, g
      
      app = Flask(__name__)
      app.secret_key = 'test'
      
      # 需求1: 所有视图都需要获取用户身份
      # 解决办法: 用钩子函数进行封装  减少代码冗余
      @app.before_request
      def prepare():
          # 必须使用g变量来传递数据, 使用全局变量不能记录并发的多个请求数据
          g.name = session.get('username')
      
      
      @app.route('/')
      def index():
          if g.name:
              return "欢迎回来, %s" % g.name
          else:
              return '首页'
      
      
      @app.route('/demo1/')
      def demo1():
          print(g.name)
          return 'demo1'
      
      
      @app.route('/login')
      def login():
          """登录"""
          session['username'] = 'zs'
          return '登录成功'
      
      
      
      if __name__ == '__main__':
          app.run(debug=True)


      2.访问限制
      需求: 对指定的路由进行访问限制
      分析:部分视图需要身份校验,这不是图每个单独校验仍会出现大量的代码冗余
      解决办法: 封装 装饰器 完成身份校验逻辑, 对指定视图函数设置装饰器
      # 访问限制代码实现及优化
      from flask import Flask, session, g, abort
      from functools import wraps
      
      app = Flask(__name__)
      app.secret_key = 'test'
      
      
      @app.before_request
      def prepare():
          g.name = session.get('username')
      
      
      @app.route('/')
      def index():
          if g.name:
              return "欢迎回来, %s" % g.name
      
          else:
              return '首页'
      
      
      @app.route('/login')
      def login():
          """登录"""
          session['username'] = 'zs'
          return '登录成功'
      
      
      # 使用装饰器封装访问限制 
      def login_required(f):  # f = user
      
          @wraps(f)  # 会将被装饰的函数(wrapper)的函数信息替换为指定函数(f)的函数信息(__name__ 函数名, __doc__ 函数注释)
          # 设置该装饰器后, 可以让闭包函数使用原函数名, 避免函数标记出现冲突(函数标记是根据函数名来生成的)
          def wrapper(*args, **kwargs):
      
              if g.name: 
                  return f(*args, **kwargs)  
      
              else: 
                  abort(401)  
      
          return wrapper
      
      
      @app.route('/user')
      @login_required  
      def user():
          """个人中心"""
          return '访问 %s 的个人中心' % g.name
      
      
      @app.route('/demo1')
      @login_required
      def demo1():
          return 'demo1'
      
      
      
      if __name__ == '__main__':
          print(app.url_map)
          app.run(debug=True)
    5. 应用配置
      主文件 main.py 从对象中加载封装的配置 app.config.from_object()
      加载隐私配置: app.config.from_envvar('ENV_CONFIG',silent=True)
      加载配置时, 设置参数 silent=True, 则配置加载失败也不会报错

      config.py文件里代码示例:

      # config.py 
      
      from datetime import timedelta
      
      
      class BaseConfig:
          """配置基类  可以将相同的配置抽取到基类中, 减少重复代码"""
      
          # 定义和配置同名的类属性
          PERMANENT_SESSION_LIFETIME = timedelta(days=7)
      
      
      class DevelopmentConfig(BaseConfig):
          """开发环境"""
          SQL_URL = '127.0.0.1:3306/test1'  # 数据库地址
      
      
      class ProductionConfig(BaseConfig):
          """生产环境"""
          SQL_URL = '222.10.15:3306/users'  # 数据库地址



      # 定义工厂函数需要的配置(方便主文件提取配置类)
      # 定义字典来记录 配置类型 和 配置子类 之间的映射关系 config_dict = { 'dev': DevelopmentConfig, 'pro': ProductionConfig }
       

      1). main.py加载配置示例:

      # main.py 
      
      from datetime import timedelta
      from flask import Flask
      
      app = Flask(__name__)
      
      # 从对象中加载配置
      # 优点: 面向对象的设计有利于 减少重复代码 以及 代码解耦合
      from config import DevelopmentConfig
      app.config.from_object(DevelopmentConfig)
      
      
      @app.route('/')
      def index():
          print(app.config.get('PERMANENT_SESSION_LIFETIME'))
          return "index"
      
      
      if __name__ == '__main__':
          app.run(debug=True)

      2). 切换配置方案: 定义工厂函数, 封装应用的创建过程; 利用环境变量,调用工厂函数, 指定配置并动态创建应用

     定义工厂函数main.py代码示例:

    from flask import Flask, current_app, Config
    from config import config_dict
    
    
    # 工厂函数: 根据参数需求, 内部封装对象的创建过程
    def create_app(config_type):
        """封装应用的创建过程"""
    
        # 创建应用
        flask_app = Flask(__name__)
    
        # 根据配置类型取出对应的配置子类
        config_class = config_dict[config_type]
    
        # 加载普通配置
        flask_app.config.from_object(config_class)
    
        return flask_app
    
    
    # 创建应用对象
    app = create_app('dev')
    
    
    @app.route("/")
    def index():
        print(app.config.get('SQL_URL'))
        return "index"
    
    
    if __name__ == '__main__':
        app.run()

      3). 加载隐私配置示例

    在项目目录外 创建隐私配置文件secret_config.py, 并以全局变量形式设置隐私配置

    # secret_config.py
    
    SECRET_KEY = 'heima123'   # 隐私配置

    主文件main.py 通过环境变量方式来加载隐私配置

    # main.py
    
    from datetime import timedelta
    from flask import Flask
    
    app = Flask(__name__)
    
    # 从环境变量中加载配置
    # 优点: 可以保护隐私配置   export ENV_CONFIG="隐私配置的文件路径"
    app.config.from_envvar('ENV_CONFIG')
    
    
    @app.route('/')
    def index():
        print(app.config.get('SECRET_KEY'))
        return "index"
    
    
    # if __name__ == '__main__':
    #     app.run(debug=True)

    环境变量&终端命令 启动程序

    $ export FLASK_APP="main"  # 设置内置环境变量
    $ export ENV_CONFIG="/xx/secret_config.py"  # 设置隐私配置对应的环境变量
    $ flask run  # 启动web程序

    实际开发中的方案: 

    • 开发阶段, 只加载普通配置
    • 生产阶段, 先加载普通配置, 再通过环境变量的方式加载项目以外的隐私配置并覆盖原有配置

    代码示例:

    def create_app(config_type):
        """封装应用的创建过程"""
    
        # 创建应用
        flask_app = Flask(__name__)
        # 根据配置类型取出对应的配置子类
        config_class = config_dict[config_type]
    
        # 先加载普通配置
        flask_app.config.from_object(config_class)
        # 再加载隐私配置  silent=True, 配置加载失败也不报错
        flask_app.config.from_envvar('ENV_CONFIG', silent=True)
    
        return flask_app

    加载配置时, 设置参数 silent=True, 则配置加载失败也不会报错

  • 相关阅读:
    每天一个linux命令(39):grep 命令
    每天一个linux命令(44):top命令
    每天一个linux命令(42):kill命令
    每天一个linux命令(41):ps命令
    每天一个linux命令(55):traceroute命令
    每天一个linux命令(45):free 命令
    为什么程序员的价值总是被严重的低估?
    每天一个linux命令(54):ping命令
    每天一个linux命令(46):vmstat命令
    每天一个linux命令(48):watch命令
  • 原文地址:https://www.cnblogs.com/yqyn-study/p/13511433.html
Copyright © 2011-2022 走看看