[Python自学] Flask框架 (3) (路由和视图)

一、路由系统

1.浅析@app.route的源码

我们使用@app.route("/index")可以给视图函数加上路由映射。我们分析一下@app.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

可以看到,装饰器的核心就是add_url_rule()函数。这里的self就是Flask的实例app。因为是app调用的route。

也就是说,我们不使用装饰器,也可以直接调用该函数实现路由映射:

def test():
    return 'test'


# 使用app.add_url_rule代替@app.route
# 第一个参数就是url,第二个参数是endpoint(即路由name),第三个参数为视图函数引用
app.add_url_rule('/test', None, test)

执行结果:

 可以看到,这种方式实现的路由,也可以正常访问。

2.分析add_url_rule函数

@setupmethod
def add_url_rule(
    self,
    rule,
    endpoint=None,
    view_func=None,
    provide_automatic_options=None,
    **options
):
    if endpoint is None:  # 如果传入的endpoint为None,则使用视图函数的__name__作为endpoint
        endpoint = _endpoint_from_view_func(view_func)
    options["endpoint"] = endpoint  # 将endpoint设置到options中
    methods = options.pop("methods", None)  # 从参数中获取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为None,就去view_func中找methods,如果找不到,则默认为GET
        methods = getattr(view_func, "methods", None) or ("GET",)
    if isinstance(methods, string_types):  # 如果methods是str,则报错,必须是列表
        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中元素全部转换为大写
    # Methods that should always be added
    required_methods = set(getattr(view_func, "required_methods", ()))  # 获取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
        provide_automatic_options = getattr(
            view_func, "provide_automatic_options", None
        )
    if provide_automatic_options is None:  # 如果还为None
        if "OPTIONS" not in methods:  # 如果methods中没有OPTIONS
            provide_automatic_options = True  # provide_automatic_options置为True
            required_methods.add("OPTIONS")
        else:
            provide_automatic_options = False  # 如果methods中有,则provide_automatic_options置为False
    # Add the required methods now.
    methods |= required_methods  # 并集
    # 将我们传入的url,endpoint,func等封装起来,成为一个Rule对象
    rule = self.url_rule_class(rule, methods=methods, **options)
    rule.provide_automatic_options = provide_automatic_options
    # 将封装好的Rule对象添加到Map类的对象中
    self.url_map.add(rule)
    if view_func is not None:  # 传入的视图函数是否为空,这里不为空
        old_func = self.view_functions.get(endpoint)  # 去view_functions字典中看看有没有同名的视图函数
        if old_func is not None and old_func != view_func:  # 如果有同名视图函数,且函数不是我们当前传入的函数,则报错
            raise AssertionError(
                "View function mapping is overwriting an "
                "existing endpoint function: %s" % endpoint  # 报错:存在同名的视图函数
            )
        # 如果endpoint没有冲突,则将视图函数加入view_functions字典中
        self.view_functions[endpoint] = view_fun

这段代码主要的功能就是,将我们传入的url、endpoint、methods等一系列路由参数,封装成一个Rule对象,然后添加到Map对象中。然后判断是否存在endpoint冲突的视图函数,如果没有,则将 endpoint:视图函数引用 键值对存放在app.view_functions字典中,该字典主要就是用来检查endpoint的冲突问题。

所以从这里可以看出,我们在写路由的时候,尽量不要让endpoint重名,如果一定要重名,则函数必须是相同的(例如两个url对应一个视图函数的场景)。例如:

@app.route('/test2', endpoint='t1')
@app.route('/test', endpoint='t1')
def test():
    return 'test'

[email protected]装饰器的参数

@app.route和app.add_url_rule参数:

    rule,   # URL规则
    view_func,   # 视图函数名称
    defaults = None,   # 默认值, 当URL中无参数,函数需要参数时,使用defaults = {'k': 'v'}为函数提供参数
    endpoint = None,   # 名称,用于反向生成URL,即: url_for('名称')
    methods = None,   # 允许的请求方式,如:["GET", "POST"]

    strict_slashes = None,   # 对URL最后的 / 符号是否严格要求,如:
        例如:
            @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

    indexredirect_to = None,  # 重定向到指定地址 如:
        例如:
            @app.route('/index/<int:nid>', redirect_to='/home/<nid>')
            或
            def func(adapter, nid):
                return "/home/888"
            @app.route('/index/<int:nid>', redirect_to=func)


    subdomain = None,  # 子域名访问,什么是子域名:主干域名是www.leeoo.com  admin.leeoo.com就是admin子域名
        例如:
            from flask import Flask, views, url_for

            app = Flask(import_name=__name__)
            # 配置服务器地址和端口
            app.config['SERVER_NAME'] = 'leeoo.com:5000'

            # 当访问admin.leeoo.com/时才会走这个路由
            @app.route("/", subdomain="admin")
            def static_index():
                """Flask supports static subdomains
                This is available at static.your-domain.tld"""
                return "static.your-domain.tld"

            # 动态子域名,访问user1.leeoo.com/dynamic,则相当于将'user1'作为参数传入username_index()视图函数
            @app.route("/dynamic", subdomain="<username>")
            def username_index(username):
                """Dynamic subdomains are also supported
                Try going to user1.your-domain.tld/dynamic"""
                return username + ".your-domain.tld"

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

4.

猜你喜欢

转载自www.cnblogs.com/leokale-zz/p/12372468.html