Flask之路由系统

一、路由本质

在flask中:

from flask import Flask

app = Flask(__name__)


@app.route('/',methods=['GET','POST'],endpoint='index')
def index():
    return 'index'


if __name__ == '__main__':
    app.run()

其中的视图函数被@app.route('/',methods=['GET','POST'],endpoint='index')装饰,在route函数中:

    def route(self, rule, **options):
        def decorator(f):
            endpoint = options.pop("endpoint", None)
            self.add_url_rule(rule, endpoint, f, **options)
            return f
        return decorator

其中self就是app对象,那么执行顺序是:

1、app.route('/',methods=['GET','POST'],endpoint='index')

执行这一步主要就是通过闭包将route的参数传递到decorator的作用域中,执行route函数返回的就是decorator函数。

2、@decorator

当执行完第一步后,视图函数就会变成下面这样:

@decorator
def index():
    return 'index'

此时index相当于被decorator修饰,在decorator函数中,endpoint如果存在就取出来,如果没有传递默认为None;然后值得注意的是:

self.add_url_rule(rule, endpoint, f, **options)  #rule是‘/’,f是index函数,**options是methods参数

这一步是将路由与视图加入到对应的映射关系中。

3、add_url_rule

用于添加路由与视图的对应关系,这也是flask中路由的本质,我们直接通过这个方法也是可以实现以上的功能:

from flask import Flask

app = Flask(__name__)


def index():
    return 'index'

app.add_url_rule('/', view_func=index, methods=['GET', 'POST'], endpoint='index')

if __name__ == '__main__':
    app.run()

4、add_url_rule参数详解

add_url_rule中有很多参数,比如:

rule #url规则
view_func #视图名称
defaults = None #缺省值为None,当URL中无参数,函数需要参数时,使用defaults={'k':'v'}为函数提供参数
endpoint = None #缺省值为None,用于反向生成URL,即: url_for('名称')
methods=None #缺省值为None,允许的请求方式,如:["GET","POST"]
redirect_to=None #缺省值为None,重定向到指定地址
strict_slashes=None #缺省值为None,对URL最后的 / 符号是否严格要求
  • defaults
from flask import Flask


app = Flask(__name__)

@app.route('/',endpoint='index',defaults={'id':10})
def index(id):
    print(id)  #接收传递的默认参数
    return 'index'

if __name__ == '__main__':
    app.run()
  • redirect_to
from flask import Flask

app = Flask(__name__)


@app.route('/index', methods=['GET', 'POST'], endpoint='index1', redirect_to="/index2")
def index():
    return 'index'


@app.route('/index2', methods=['GET', 'POST'], endpoint='index2')
def index2():
    return 'index2'


if __name__ == '__main__':
    app.run()
  • strict_slashes
from flask import Flask

app = Flask(__name__)

@app.route('/index',strict_slashes=False) #访问http://127.0.0.1:5000/index 或http://127.0.0.1:5000/index/  均可
def index():
    return 'index'


@app.route('/home',strict_slashes=True) #只能访问 http://127.0.0.1:5000/home
def home():
    return 'home'

if __name__ == '__main__':
    app.run()

5、正则路由匹配

  在Flask中的路由是有一些变量规则的,通过把 URL 的一部分标记为<variable_name> 就可以在 URL 中添加变量。标记的 部分会作为关键字参数传递给函数。通过使用  <converter:variable_name>,可以 选择性的加上一个转换器,为变量指定规则。请看下面的例子:

@app.route('/user/<username>')
def show_user_profile(username):
    # show the user profile for that user
    return 'User %s' % escape(username)

@app.route('/post/<int:post_id>')
def show_post(post_id):
    # show the post with the given id, the id is an integer
    return 'Post %d' % post_id

@app.route('/path/<path:subpath>')
def show_subpath(subpath):
    # show the subpath after /path/
    return 'Subpath %s' % escape(subpath)

转换器类型:

string (缺省值) 接受任何不包含斜杠的文本
int 接受正整数
float 接受正浮点数
path 类似 string ,但可以包含斜杠
uuid 接受 UUID 字符串

但是如果自己想指定规则,那么可以这样做:

from flask import Flask, url_for
from werkzeug.routing import BaseConverter
import re

app = Flask(__name__)


# 1、自定义RegexConvert
class RegecConvert(BaseConverter):

    def __init__(self, map, regex):
        super(RegecConvert, self).__init__(map)
        self.regex = regex

    def to_python(self, value):
        """进行路由匹配,匹配成功后返回给视图函数中的参数"""
        value = re.match(self.regex, value).group()
        return int(value)

    def to_url(self, value):
        """使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数"""
        val = super(RegecConvert, self).to_url(value)
        return val


# 2、将RegecConvert加入到flask中
app.url_map.converters['regex'] = RegecConvert  # 下次再使用该规则,就使用regex,这与int/float意思一样


@app.route('/index/<regex("d+"):id>', endpoint='index')
def index(id):
    print(id, type(id))  # 这个id是经过to_python方法匹配处理后的结果
    url_for('index', id=10)  # 这个id的值传递给to_url方法进行处理
    return 'index'


if __name__ == '__main__':
    app.run()

二、CBV

from flask import Flask, views

app = Flask(__name__)


def auth(func):
    def inner(*args, **kwargs):
        result = func(*args, **kwargs)
        return result

    return inner


class IndexView(views.MethodView):
    method = ['GET', ]
    decorators = [auth, ]

    def get(self):
        return 'get'

    def post(self):
        return 'post'


app.add_url_rule('/', view_func=IndexView.as_view(name='index'))

if __name__ == '__main__':
    app.run()

启动web服务在5000端口,然后访问‘/’路径,就会执行IndexView.as_view(name='index'),显然会去继承的父类中寻找:

class View(object):
    methods = None
    provide_automatic_options = None
    decorators = ()

    @classmethod
    def as_view(cls, name, *class_args, **class_kwargs):

        def view(*args, **kwargs):
            self = view.view_class(*class_args, **class_kwargs)
            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_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-->dispatch_request,在dispatch_request进行反射和分发。

class MethodView(with_metaclass(MethodViewType, View)):
  
    def dispatch_request(self, *args, **kwargs):
        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.
        if meth is None and request.method == "HEAD":
            meth = getattr(self, "get", None)

        assert meth is not None, "Unimplemented method %r" % request.method
        return meth(*args, **kwargs)

 至于IndexView中的decorators相当于装饰器,可以看到源码中进行循环所有的装饰器函数分别执行再赋值。

原文地址:https://www.cnblogs.com/shenjianping/p/13216913.html