zoukankan      html  css  js  c++  java
  • Flask

    Flask框架简介

    说明:
    flask是一个轻量级的web框架,被称为微型框架。只提供了一个高效稳定的核心,其它全部通过扩展来实现。意思就是你可以根据项目需要进行量身定制,也意味着你需要不断学习相关的扩展库。

    核心:

    WSGI系统、调试、路由

    模板引擎(Jinja2,是flask核心开发者人员发开的)

    安装:

    >: pip install flask

    werkzeug简介

    Flask的socket是基于Werkzeug 实现的

    Werkzeug是一个WSGI工具包,他可以作为一个Web框架的底层库。这里稍微说一下, werkzeug 不是一个web服务器,也不是一个web框架,而是一个工具包,官方的介绍说是一个 WSGI 工具包,它可以作为一个 Web 框架的底层库,因为它封装好了很多 Web 框架的东西,例如 Request,Response 等等

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

    flask的使用

    Flask运行的本质:

    from flask import Flask
    # 实例化产生一个Flask对象
    app = Flask(__name__)
    # 装饰器路由
    @app.route('/')
    def index():
        return 'ok'
    
    if __name__ == '__main__':
        # 本质是 run_simple(host, port, app, **options)
        # app() 调用的是Flask的__call__里的wsgi_app方法,返回response
        app.run()

    启动参数
    参数 说明
    debug 是否开启调试模式,默认为False;开启后会有出错调试信息,文件会自动加载。
    threaded 是否开启多线程,默认为Flase。
    host 指定主机,默认为’127.0.0.1’,设置为’0.0.0.0’后可以通过IP进制访问
    port 指定端口,默认为5000。
    启动示例:app.run(debug=True, threaded=True, host=‘0.0.0.0’, port=5555)

    响应四剑客 render_template,redirect,jsonify

    • 返回字符串

    @app.route('/')
    def index():
        # 1 直接返回字符串
        return 'ok'
    • 返回HTML 

    from flask import Flask, render_template
    app = Flask(__name__)
    
    @app.route('/')
    def index():
        # 2 返回HTML
        return render_template('index.html')  # 参数: xxx.HTML
    • 跳转页面(重定向)

    from flask import Flask, redirect
    app = Flask(__name__)
    
    @app.route('/')
    def index():
        # 3 跳转页面
        return redirect('/login')  # 参数: url
    
    @app.route('/login')
    def login():
        return render_template('login.html')  
    • 返回json数据

    from flask import Flask, jsonify
    app = Flask(__name__)
    
    @app.route('/')
    def index():
        # 4 返回json数据
        dic = {'name':'xionger', 'age': 20}
        return jsonify(dic)  # 参数: 要序列化的数据

    templates(模板层)

    1. 创建templates文件夹
    2. 文件夹内创建HTML文件
    • 视图函数向HTML页面传递数据

    # 方式一
    @app.route('/') def index(): dic = {'name': 'xionger', 'age': 20} return render_template('index.html', num=100, dic_data=dic)
    # 方式二
    from flask import Flask, render_template, Markup
    app = Flask(__name__)
    
    def func(t1, t2):
        return Markup(f'<h1>hahaha{t1}{t2}</h1>')
    
    @app.route('/list',methods=['GET'])
    def list():
        return render_template('list.html',html="<h1>hahaha</h1>", func=func)
    
    # list.html
        {{ html|safe }}
        {{ func(1, 2) }}
    • 模板语法

    <body>
        <p>{{ num }}</p>  100
        <p>{{ dic_data.name }}</p>  xionger
        <p>{{ dic_data.get('name') }}</p>  xionger
        <p>{{ dic_data['name'] }}</p>  xionger
    </body>
    <!-- 字典, 列表等数据类型在python中如何使用, 在模板中就如何使用 -->
    <!-- 遍历循环 -->
    <body>
        <h1>用户列表</h1>
        <table>
            {% for k,v in user_dict.items() %}
            <tr>
                <td>{{k}}</td>
                <td>{{v.name}}</td>
                <td>{{v['name']}}</td>
                <td>{{v.get('name')}}</td>
                <td><a href="/detail/{{k}}">查看详细</a></td>
            </tr>
            {% endfor %}
        </table>
    </body>
    <!-- 逻辑判断 -->
    <body>
        <h1>用户列表</h1>
        <table>
            {% if name %}
              <h1>Hello {{ name }}!</h1>
            {% else %}
              <h1>Hello World!</h1>
            {% endif %}
        </table>
    </body>

    配置文件

    • 方式一

    # 这中方式只能配置两种
    app.debug=True
    app.secret_key="123123"
    • 方式二 :使用config字典

    app.config['DEBUG']=True
    • 方式三: 导入文件(插拔式)

    settings.py
    # DEBUG = True
    
    class Config(object):
        DEBUG = False
        TESTING = False
        DATABASE_URI = 'sqlite://:memory:'
    
    
    class ProductionConfig(Config):
        DATABASE_URI = 'mysql://user@localhost/foo'
    
    
    class DevelopmentConfig(Config):
        DEBUG = True
    
    
    class TestingConfig(Config):
        TESTING = True
    视图函数文件.py
    app.config.from_object('settings.DevelopmentConfig')

    补充:

    flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为:
    {
            '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,
        }

    路由系统

    @setupmethod
        def add_url_rule(
            self,
            rule,  # --> 装饰器里的路由
            endpoint=None,  # --> 路由别名
            view_func=None,  # --> 视图函数
            provide_automatic_options=None,
            **options  # 用来接收methods等参数的字典
        ):
            
            if endpoint is None:  # 若路由没有起别名
                # endpoint = 视图函数的名字
                endpoint = _endpoint_from_view_func(view_func)
            # 将endpoint添加到options里
            options["endpoint"] = endpoint
            # 获取methods的值
            methods = options.pop("methods", None)
    
            # if the methods are not given and the view_func object knows its
            # methods we can use that instead.  If neither exists, we go with
            # a tuple of only ``GET`` as default.
            if methods is None:  
                # methods为空 则默认是GET请求
                methods = getattr(view_func, "methods", None) or ("GET",)
            if isinstance(methods, string_types):
                raise TypeError(
                    "Allowed methods have to be iterables of strings, "
                    'for example: @app.route(..., methods=["POST"])'
                )
            methods = set(item.upper() for item in methods)
    
            # Methods that should always be added
            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
    
            rule = self.url_rule_class(rule, methods=methods, **options)
            rule.provide_automatic_options = provide_automatic_options
    
            self.url_map.add(rule)
            if view_func is not None:
                old_func = self.view_functions.get(endpoint)  # endpoint 是视图函数的名字
                if old_func is not None and old_func != view_func:
                    raise AssertionError(
                        "View function mapping is overwriting an "
                        "existing endpoint function: %s" % endpoint
                    )
                # 若view_functions中有endpoint 则取出赋值给view_func
                # view_func要么必须有值, 要么endpoint有别名, 最终endpoint的值也会赋值给view_func
                self.view_functions[endpoint] = view_func  
                # 如果endpoint有别名 view_func = endpoint
                # 如果endpoint没有有别名 view_func = endpoint(视图函数名字)
    路由系统源码
    • 典型装饰器写法

    from flask import Flask
    app = Flask(__name__)
    
    # 路由装饰器配置该函数的路由
    @app.route('/index/<int:nid>',methods=['GET'],endpoint='haha')
    def index(nid):
        return nid
    '''
    '/index/<int:nid>'  # 路由有名分组,视图函数要接收
    methods  # 该视图函数可以用的请求方式
    endpoint  # 路由别名反向解析, 在其他视图函数中用: real_url=url_for("别名") 调用该函数
          endpoint默认指定的是函数名字 核心源码: route -> decorator -> add_url_rule flask路由基于装饰器,本质是基于:add_url_rule add_url_rule 源码中,endpoint如果为空, endpoint = _endpoint_from_view_func(view_func),最终取view_func.__name__(函数名)
    '''
    • add_url_rule 

    app.add_url_rule参数:
    - rule, URL规则
    - view_func, 视图函数名称
    - defaults = None, 默认值, 当URL中无参数,函数需要参数时,使用defaults = {'k': 'v'}为函数提供参数
    - endpoint = None, 名称,用于反向生成URL,即: url_for('名称')
    - methods = None, 允许的请求方式,如:["GET", "POST"]
    - redirect_to = '/login'  重定向到指定的地址 可以是路径也可以是别名
    - strict_slashes  严格模式
            @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
    def index(nid):
        return nid
    
    app.add_url_rule('/index/<string:nid>',view_func=index,endpoint="haha",methods=['POST',"GET"])

    补充: 别名使用

    def login():
        return 'login'
    app.add_url_rule('/login',view_func=login, endpoint="haha", methods=['POST',"GET"])
    
    
    def index(nid):
        real_url = url_for('haha')
    print(real_url) # /login
    return real_url app.add_url_rule('/index/<string:nid>',view_func=index,methods=['POST',"GET"])

    注意: 别名不能重复,一个路由一个别名
    •  支持正则

    1 写类,继承BaseConverter
    2 注册:app.url_map.converters['regex'] = RegexConverter
    3 使用:@app.route('/index/<regex("d+"):nid>')  正则表达式会当作第二个参数传递到类中
    from flask import Flask, url_for
    app = Flask(__name__)
    
    from werkzeug.routing import BaseConverter
    
    class RegexConverter(BaseConverter):
        """
        自定义URL匹配正则表达式
        """
        def __init__(self, map, regex):
            super(RegexConverter, self).__init__(map)
            self.regex = regex
    
        def to_python(self, value):
            """
            路由匹配时,匹配成功后传递给视图函数中参数的值
            """
            #value就正则匹配出来的结果
            # print('value',value,type(value))
            return value
    
        def to_url(self, value):
            """
            使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
            """
            val = super(RegexConverter, self).to_url(value)
            # print(val)  # 666
            return val
    
    app.url_map.converters['regex'] = RegexConverter
    @app.route('/test/<regex("d+"):nid>',endpoint="tes")
    def test(nid):
        print("nid",nid,type(nid))
        print(url_for('tes', nid='666'))  # /test/666
        return 'test'

    CBV

    as_view 源码

    @classmethod
        def as_view(cls, name, *class_args, **class_kwargs):  # cls = 视图类 name一定要传值
    
            def view(*args, **kwargs):
                # view_class = cls 视图类
                self = view.view_class(*class_args, **class_kwargs)  # self 是视图类对象
                # self调用dispatch_request方法,调用的是视图类中的dispatch_request方法
                return 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.view_class = cls
            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
    as_view -> view(View中的) -> dispatch_request(视图类中的)
    • 普通使用

    from flask import Flask, views
    app = Flask(__name__)
    
    class IndexView(views.View):
        methods = ['POST', 'GET']
        # decorators = [ ]
        def dispatch_request(self):
            print('Index')
            return 'Index'
    
    app.add_url_rule('/index', view_func=IndexView.as_view(name='index'))
    '''
    methods = []  视图类的请求方式, 默认GET, 若指定了请求方式,就只能用指定的方式请求该视图类
    as_view(name='index')  要指定name = 别名, 若不指定,导致所有的路由都是view,别名一样报错
    
    '''

    MethodView 源码

    class MethodView(with_metaclass(MethodViewType, View)):
        # 重写dispatch_request方法
        def dispatch_request(self, *args, **kwargs):  # self是视图类的对象
            # 从self中获取request中的请求方式,并改成小写
            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.
            # 如果 meth是空并且请求方式是HEAD
            if meth is None and request.method == "HEAD":
                # 从self中获取get方法
                meth = getattr(self, "get", None)
            # meth为空抛出错误
            assert meth is not None, "Unimplemented method %r" % request.method
            # 当前端的请求方式在视图类中有时,
            # meth不为空,并调用meth映射的请求方式,返回
            # 否则meth为空抛出异常
            return meth(*args, **kwargs)
    as_view -> view(View中的) -> dispatch_request(MethodView中的)
    • 指定post / get

    class LoginView(views.MethodView):
        # methods = ['GET']  # 默认支持 GET/POST ,若指定请求方式,该视图类只能用指定的方式
        def get(self):
            print('get')
            return 'get'
        def post(self):
            print('post')
            return 'post'
    
    app.add_url_rule('/login', view_func=LoginView.as_view(name='login'))
  • 相关阅读:
    Luogu 1080 【NOIP2012】国王游戏 (贪心,高精度)
    Luogu 1314 【NOIP2011】聪明的质检员 (二分)
    Luogu 1315 【NOIP2011】观光公交 (贪心)
    Luogu 1312 【NOIP2011】玛雅游戏 (搜索)
    Luogu 1525 【NOIP2010】关押罪犯 (贪心,并查集)
    Luogu 1514 引水入城 (搜索,动态规划)
    UVA 1394 And Then There Was One / Gym 101415A And Then There Was One / UVAlive 3882 And Then There Was One / POJ 3517 And Then There Was One / Aizu 1275 And Then There Was One (动态规划,思维题)
    Luogu 1437 [HNOI2004]敲砖块 (动态规划)
    Luogu 1941 【NOIP2014】飞扬的小鸟 (动态规划)
    HDU 1176 免费馅饼 (动态规划)
  • 原文地址:https://www.cnblogs.com/waller/p/11837979.html
Copyright © 2011-2022 走看看