zoukankan      html  css  js  c++  java
  • python框架之Flask(1)-Flask初使用

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

    准备

    安装

    pip3 install flask

    werkzeug的简单使用

    from werkzeug.wrappers import Request, Response
    from werkzeug.serving import run_simple
    
    
    @Request.application
    def hello(request):
        return Response('Hello World!')
    
    
    if __name__ == '__main__':
        run_simple('localhost', 4000, hello)

    使用

    hello flask

    from flask import Flask
    
    app = Flask(__name__)
    
    
    @app.route('/')
    def hello_world():
        return 'Hello World!'
    
    
    if __name__ == '__main__':
        app.run()

    登录示例

    from flask import Flask, request, render_template, redirect, session
    
    app = Flask(__name__,
                template_folder='templates',  # 默认模板文件夹
                static_folder='static',  # 默认静态文件文件夹
                static_url_path='/static'  # 默认静态文件访问路径
                )
    app.config['DEBUG'] = True
    # 使用 session 必须定义 否则报错
    app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
    
    
    # 默认只支持 GET 请求
    @app.route('/login', methods=['GET', "POST"])
    def login():
        if request.method == 'GET':
            # 渲染模板 默认对应templates 文件夹
            return render_template('login.html')
        user = request.form.get('user')
        pwd = request.form.get('pwd')
    
        if user == 'zze' and pwd == '666':
            session['user'] = user
            # 重定向
            return redirect('/index')
        return render_template('login.html', msg='用户名或密码错误!')
    
    
    @app.route('/index')
    def index():
        user = session['user']
        if not user:
            return redirect('/login')
        return render_template('index.html')
    
    
    if __name__ == '__main__':
        app.run()

    配置文件

    • 方式一:app.config

      app.config 本质上其实是一个字典,所以可以通过如下方式进行配置:

      from flask import Flask
      
      app = Flask(__name__)
      app.config['DEBUG'] = True
    • 方式二:对象配置

      当然,Flask 也提供了单文件配置的方式,如下:

      class Dev:
          DEBUG = True
      settings.py
      from flask import Flask
      
      app = Flask(__name__)
      app.config.from_object('settings.Dev')
    • 方式三:文件配置

      app.config.from_pyfile("settings.py")
      # 如:
      # settings.py
      #    DEBUG = True
    • 方式四:环境变量

      app.config.from_envvar("环境变量名称")
      # 环境变量的值为python文件名称名称,内部调用from_pyfile方法
    • 方式五:json方式

      app.config.from_json("json文件名称")
      # json文件名称,必须是json格式,因为内部会执行json.loads
    • 方式六:字典方式

      app.config.from_mapping({'DEBUG':True})
      # 字典格式
    • 默认配置参数

      {
              'DEBUG':                                get_debug_flag(default=False),  是否开启Debug模式
              'TESTING':                              False,                          是否开启测试模式
              'PROPAGATE_EXCEPTIONS':                 None,                         
              'PRESERVE_CONTEXT_ON_EXCEPTION':        None,
              'SECRET_KEY':                           None,
              'PERMANENT_SESSION_LIFETIME':           timedelta(days=31),
              'USE_X_SENDFILE':                       False,
              'LOGGER_NAME':                          None,
              'LOGGER_HANDLER_POLICY':               'always',
              'SERVER_NAME':                          None,
              'APPLICATION_ROOT':                     None,
              'SESSION_COOKIE_NAME':                  'session',
              'SESSION_COOKIE_DOMAIN':                None,
              'SESSION_COOKIE_PATH':                  None,
              'SESSION_COOKIE_HTTPONLY':              True,
              'SESSION_COOKIE_SECURE':                False,
              'SESSION_REFRESH_EACH_REQUEST':         True,
              'MAX_CONTENT_LENGTH':                   None,
              'SEND_FILE_MAX_AGE_DEFAULT':            timedelta(hours=12),
              'TRAP_BAD_REQUEST_ERRORS':              False,
              'TRAP_HTTP_EXCEPTIONS':                 False,
              'EXPLAIN_TEMPLATE_LOADING':             False,
              'PREFERRED_URL_SCHEME':                 'http',
              'JSON_AS_ASCII':                        True,
              'JSON_SORT_KEYS':                       True,
              'JSONIFY_PRETTYPRINT_REGULAR':          True,
              'JSONIFY_MIMETYPE':                     'application/json',
              'TEMPLATES_AUTO_RELOAD':                None,
      }

    路由系统

    @app.route('/user/<username>')
    @app.route('/post/<int:post_id>')
    @app.route('/post/<float:post_id>')
    @app.route('/post/<path:path>')
    @app.route('/login', methods=['GET', 'POST'])

    常用路由系统有以上五种,所有的路由系统都是基于一下对应关系来处理:

    DEFAULT_CONVERTERS = {
        'default':          UnicodeConverter,
        'string':           UnicodeConverter,
        'any':              AnyConverter,
        'path':             PathConverter,
        'int':              IntegerConverter,
        'float':            FloatConverter,
        'uuid':             UUIDConverter,
    }

    可以通过 endpoint 和 url_for 反向生成:

    from flask import Flask, url_for
    
    app = Flask(__name__)
    
    
    @app.route('/index', endpoint='i')
    def index():
        return url_for('i')  # endpoint 值未指定时,默认为方法名
    
    
    if __name__ == '__main__':
        app.run()

    模板

    • 语法

      Flask 使用的是 Jinjia2 模板,所以其语法和在 Django 中使用时无差别。

      Markup 等价 django 的 mark_safe。

    • 模板方法

      {% macro input(name, type='text', value='') %}
          <input type="{{ type }}" name="{{ name }}" value="{{ value }}">
      {% endmacro %}
      
      {{ input('n1') }}

    请求和响应

    • 请求相关

      request 内置属性:
          request.method # 方法
          request.args # GET 请求时的参数
          request.form # POST 请求时的参数
          request.values # 所有参数
          request.cookies # cookie 内容
          request.headers # 请求头信息
          request.path # 请求路径
          request.full_path # 请求全路径
      
          路径相关:
              request.script_root
              request.url
              request.base_url
              request.url_root
              request.host_url
              request.host
          
          request.files # 上传文件接收 
      # 保存文件
      obj = request.files['the_file_name']
      obj.save('/var/www/uploads/' + secure_filename(f.filename))
    • 响应相关

      # 返回字符串
      return "字符串"
      # 返回渲染模板
      return render_template('html模板路径',**{})
      # 重定向
      return redirect('/index.html')

    Session

    当请求刚到来时,flask 读取 cookie 中 session 对应的值,并将该值解密并反序列化成字典,放入内存以便视图函数使用。

    当请求结束时,flask 会读取内存中字典的值,进行序列化和加密,再写入到 cookie 中。

    中间件

    从 flask 的入口 app.run 方法看起,

     1 def run(self, host=None, port=None, debug=None,
     2         load_dotenv=True, **options):
     3     if os.environ.get('FLASK_RUN_FROM_CLI') == 'true':
     4         from .debughelpers import explain_ignored_app_run
     5         explain_ignored_app_run()
     6         return
     7 
     8     if get_load_dotenv(load_dotenv):
     9         cli.load_dotenv()
    10 
    11         # if set, let env vars override previous values
    12         if 'FLASK_ENV' in os.environ:
    13             self.env = get_env()
    14             self.debug = get_debug_flag()
    15         elif 'FLASK_DEBUG' in os.environ:
    16             self.debug = get_debug_flag()
    17 
    18     if debug is not None:
    19         self.debug = bool(debug)
    20 
    21     _host = '127.0.0.1'
    22     _port = 5000
    23     server_name = self.config.get('SERVER_NAME')
    24     sn_host, sn_port = None, None
    25 
    26     if server_name:
    27         sn_host, _, sn_port = server_name.partition(':')
    28 
    29     host = host or sn_host or _host
    30     port = int(port or sn_port or _port)
    31 
    32     options.setdefault('use_reloader', self.debug)
    33     options.setdefault('use_debugger', self.debug)
    34     options.setdefault('threaded', True)
    35 
    36     cli.show_server_banner(self.env, self.debug, self.name, False)
    37 
    38     from werkzeug.serving import run_simple
    39 
    40     try:
    41         run_simple(host, port, self, **options)
    42     finally:
    43         self._got_first_request = False
    flask.app.Flask.run

    直接看到 41 行,这里的 run_simple 方法实际上就是 werkzeug.serving.run_simple ,而传入的第三个参数 self 实际上就是指的 app 本身。我们先要了解的是,在上面的werkzeug的简单使用中,传入的是一个方法,并且这个方法会在请求到来时被执行。而在这里传入一个对象,加 () 执行一个对象时实际上是执行这个对象的 __call__ 方法,查看 app.__call__ 方法:

    1 def __call__(self, environ, start_response):
    2     return self.wsgi_app(environ, start_response)
    flask.app.Flask.__call__

    看到这里我们可以确定,后续整个程序的执行就是依赖这第 2 行的触发,而我们只要在第二行执行之前定义的处理就相当于中间件的前置处理,在它之后的处理就相当于后置处理了。所以可以通过如下方法实现中间件的功能:

    from flask import Flask
    app
    = Flask(__name__) class MiddleWare: def __init__(self, wsgi_app): self.wsgi_app = wsgi_app def __call__(self, *args, **kwargs): print('前置处理') obj = self.wsgi_app(*args, **kwargs) print('后置处理') if __name__ == "__main__": app.wsgi_app = MiddleWare(app.wsgi_app) app.run(port=9999)

    flash

    flash 就是用来存储只使用一次的数据,类似于将数据放入列表,然后通过 pop 方法取出。

    flash(message, category='message')
    get_flashed_messages(with_categories=False, category_filter=[])
    • 使用

      from flask import Flask, flash, get_flashed_messages
      
      app = Flask(__name__)
      
      
      @app.route('/page1')
      def page1():
          flash('临时数据')
          return 'page1'
      
      
      @app.route('/page2')
      def page2():
          get_flashed_messages()
          return 'page2'
      
      
      if __name__ == '__main__':
          app.run()
    • 源码分析

      查看 flash 方法:
      1 def flash(message, category='message'):
      2     flashes = session.get('_flashes', [])
      3     flashes.append((category, message))
      4     session['_flashes'] = flashes
      5     message_flashed.send(current_app._get_current_object(),
      6                          message=message, category=category)
      flask.helpers.flash

      可以看到它其实就是把 message 和 category 以元组的形式存储在 session 中键为 _flashes 的值中。

      再看 get_flashed_messages 方法:
       1 def get_flashed_messages(with_categories=False, category_filter=[]):
       2     flashes = _request_ctx_stack.top.flashes
       3     if flashes is None:
       4         _request_ctx_stack.top.flashes = flashes = session.pop('_flashes') 
       5             if '_flashes' in session else []
       6     if category_filter:
       7         flashes = list(filter(lambda f: f[0] in category_filter, flashes))
       8     if not with_categories:
       9         return [x[1] for x in flashes]
      10     return flashes
      flask.helpers.get_flashed_messages

      从第 4 行可以看到每次获取 flash 数据时,就是从 session 中将之前存入的键为 _flashes 的元素 pop 出来,然后进行过滤返回对应 category 内容。

    特殊的装饰器

    • before_request&after_request

      from flask import Flask, flash, get_flashed_messages
      
      app = Flask(__name__)
      app.secret_key = '123123'
      
      
      @app.before_request
      def before_request1():
          print('before_request1')
      
      
      @app.before_request
      def before_request2():
          print('before_request2')
      
      
      @app.after_request
      def after_request1(response):
          print('after_request1')
          return response
      
      
      @app.after_request
      def after_request2(response):
          print('after_request2')
          return response
      
      
      @app.route('/index')
      def index():
          print('index')
          return 'index'
      
      
      if __name__ == '__main__':
          app.run()
          
      # 执行顺序如下:
      #     before_request1
      #     before_request2
      #     index
      #     after_request2
      #     after_request1
    • template_filter

      from flask import Flask, flash, get_flashed_messages, render_template
      
      app = Flask(__name__)
      app.secret_key = '123123'
      
      
      @app.template_filter()
      def add_filter(a, b):
          return a + b
      
      
      @app.route('/index')
      def index():
          print('index')
          return render_template('index.html')
      
      
      if __name__ == '__main__':
          app.run()
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>Title</title>
      </head>
      <body>
      {{1|add_filter(2)}}
      </body>
      </html>
    • template_global

      from flask import Flask, flash, get_flashed_messages, render_template
      
      app = Flask(__name__)
      app.secret_key = '123123'
      
      
      @app.template_global()
      def add_template_func(a, b):
          return a + b
      
      
      @app.route('/index')
      def index():
          print('index')
          return render_template('index.html')
      
      
      if __name__ == '__main__':
          app.run()
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>Title</title>
      </head>
      <body>
      {{add_template_func(1,2)}}
      </body>
      </html>
    • errorhandler

      @app.errorhandler(404)
      def page_not_found(error):
          return render_template('404.html'),404
    • before_first_request

      @app.before_first_request
      def application_start():
          # 同 before_request,但仅在第一次请求时执行一次。
          pass
  • 相关阅读:
    什么是test-time argument(测试数据增强)
    在k3d上快速安装Istio,助你在本地灵活使用K8S!
    IoT设备实践丨如果你也在树莓派上部署了k3s,你也许需要这篇文章
    k3s原理分析丨如何搞定k3s node注册失败问题
    仅需60秒,使用k3s创建一个多节点K8S集群!
    手把手实操教程!使用k3s运行轻量级VM
    k3s首季在线培训来袭!本周四晚,线上见!
    k3s新版本发布!支持Helm3!还有其他重要更新Highlight!
    如何优雅地使用containerd?这里有一份必读的技巧攻略
    图解拥塞控制,这应该是把拥塞控制讲的最通俗易懂的文章了
  • 原文地址:https://www.cnblogs.com/zze46/p/10104887.html
Copyright © 2011-2022 走看看