Flask - 四剑客 | templates | 配置文件 | 路由系统 | CBV

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'))

 

 

posted @ 2019-11-11 20:23  waller  阅读(411)  评论(0编辑  收藏  举报