美文网首页Pythoner集中营FLask初探Web开发
Flask初探五( Blueprint / url_for /

Flask初探五( Blueprint / url_for /

作者: DragonFangQy | 来源:发表于2018-06-30 16:27 被阅读52次

    Blueprint (蓝图)

    Blueprints are the recommended way to implement larger or more
    pluggable applications in Flask 0.7 and later.

    Blueprint 是为了更方便实现模块化开发而诞生的.

    模块化

    为什么要模块化?

    在一个py 文件中写成百上千或者更多个接口, 相信大部分或者绝大部分人都不愿意维护这样的代码, 其次所有接口都写在一个文件中不利于团队协作. 基于以上两点,分模块 / 团队协作 的模块化就显的很有必要了.

    Blueprint 与模块

    一般来说,都会以功能进行模块划分, 这里就不讨论如何划分了. 假设有首页 / 新闻页 / 登录 三个模块.

    原始写法

    from flask import Flask
    
    app = Flask(__name__)
    
    
    @app.route('/')
    def index():
        return 'index'
    
    
    @app.route('/news')
    def index():
        return 'index'
    
    
    @app.route('/login')
    def index():
        return 'index'
    
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    

    接口通过app.route 将规则 和 视图函数进行绑定, 存在同名函数,绑定失败

    **导入Blueprint **

    # 导入Blueprint
    from flask import Blueprint
    from flask import Flask
    
    # 实例化Blueprint 对象
    #                       蓝图名, import_name ,规则前缀
    index_blue = Blueprint("index", __name__, url_prefix="/index")
    news_blue = Blueprint("news", __name__, url_prefix="/news")
    login_blue = Blueprint("login", __name__, url_prefix="/login")
    
    app = Flask(__name__)
    
    
    # 用Blueprint 对象实现接口
    @index_blue.route('/')
    def index():
        return 'index'
    
    
    @news_blue.route('/')
    def index():
        return 'news'
    
    
    @login_blue.route('/')
    def index():
        return 'index'
    
    
    # 注册Blueprint 
    app.register_blueprint(index_blue)
    app.register_blueprint(news_blue)
    app.register_blueprint(login_blue)
    
    if __name__ == '__main__':
        print(app.url_map)
        app.run(debug=True)
    
    

    运行结果

    Map([<Rule '/index/' (HEAD, GET, OPTIONS) -> index.index>,
     <Rule '/login/' (HEAD, GET, OPTIONS) -> login.index>,
     <Rule '/news/' (HEAD, GET, OPTIONS) -> news.index>,
     <Rule '/static/<filename>' (HEAD, GET, OPTIONS) -> static>])
    

    Blueprint 的使用步骤

    • 实例化Blueprint 对象, 需要导入Blueprint 类
    • 用Blueprint 对象实现实现接口
    • 注册Blueprint

    虽然用Blueprint 实现了接口,但是接口依然在一个文件中, 接下来进行拆分

    模块化

    01-模块化后的项目目录.png

    main.py

    from flask import Flask
    
    from modules import index_blue, news_blue, login_blue
    
    app = Flask(__name__)
    
    # 注册蓝图
    app.register_blueprint(index_blue)
    app.register_blueprint(news_blue)
    app.register_blueprint(login_blue)
    
    if __name__ == '__main__':
        print(app.url_map)
        app.run(debug=True)
    
    

    __init__.py

    # 导入Blueprint
    from flask import Blueprint
    
    # 实例化蓝图对象
    index_blue = Blueprint("index", __name__, url_prefix="/index")
    news_blue = Blueprint("news", __name__, url_prefix="/news")
    login_blue = Blueprint("login", __name__, url_prefix="/login")
    
    from . import index
    from . import news
    from . import login
    
    

    index.py

    from . import index_blue
    
    
    @index_blue.route('/')
    def index():
        return 'index'
    
    

    login.py

    from . import login_blue
    
    
    @login_blue.route('/')
    def index():
        return 'login'
    
    

    news.py

    from . import news_blue
    
    
    @news_blue.route('/')
    def index():
        return 'news'
    
    

    运行结果

    Map([<Rule '/index/' (HEAD, GET, OPTIONS) -> index.index>,
     <Rule '/login/' (HEAD, GET, OPTIONS) -> login.index>,
     <Rule '/news/' (HEAD, GET, OPTIONS) -> news.index>,
     <Rule '/static/<filename>' (HEAD, GET, OPTIONS) -> static>])
    

    蓝图的作用原理

    猜测: 未使用蓝图时, url_map 存放的是形如 <Rule '/index/' (HEAD, GET, OPTIONS) -> index> 这样的Rule 对象使用蓝图之后,url_map 存放的是形如 <Rule '/index/' (HEAD, GET, OPTIONS) -> index.index> 这样的Rule 对象.
    所以蓝图是通过改变视图函数的端点的方式区分视图函数的.

    endpoint
    flask初探二 中简单的探究了endpoint, 这里将探究蓝图与endpoint 之间的关系

    蓝图的route

        def route(self, rule, **options):
            """Like :meth:`Flask.route` but for a blueprint.  The endpoint for the
            :func:`url_for` function is prefixed with the name of the blueprint.
            """
            def decorator(f):
                endpoint = options.pop("endpoint", f.__name__)
                self.add_url_rule(rule, endpoint, f, **options)
                return f
            return decorator
    

    和 flask 的route 基本一样

    蓝图的add_url_rule

        def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
            """Like :meth:`Flask.add_url_rule` but for a blueprint.  The endpoint for
            the :func:`url_for` function is prefixed with the name of the blueprint.
            """
            if endpoint:
                assert '.' not in endpoint, "Blueprint endpoint's should not contain dot's"
            self.record(lambda s:
                s.add_url_rule(rule, endpoint, view_func, **options))
    
    

    BlueprintSetupState 的add_url_rule

       def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
            """A helper method to register a rule (and optionally a view function)
            to the application.  The endpoint is automatically prefixed with the
            blueprint's name.
            """
            if self.url_prefix:
                rule = self.url_prefix + rule
            options.setdefault('subdomain', self.subdomain)
            if endpoint is None:
                endpoint = _endpoint_from_view_func(view_func)
            defaults = self.url_defaults
            if 'defaults' in options:
                defaults = dict(defaults, **options.pop('defaults'))
            # 重点在这
            self.app.add_url_rule(rule, '%s.%s' % (self.blueprint.name, endpoint),
                                  view_func, defaults=defaults, **options)
    
    

    循着源码的运行,可以看到蓝图最终是通过调用 app.add_url_rule 的方式将规则和视图函数进行的绑定, 区别是在调用的时候对endpoint 进行的传值, 从而印证了我们开始的猜测.

    结论

    • 蓝图是通过改变endpoint 的方式区分视图函数的.
    • endpoint 默认情况下是视图函数名,
    • 使用蓝图后, endpoint 为 蓝图名.视图函数名

    endpoint 与 url_for

    url_for

    def url_for(endpoint, **values):
    ...(省略)...
    

    从url_for 方法的定义可以看出, url_for需要的是endpoint

    • 在不使用蓝图的情况下, 可以直接使用函数名字符串得到URL
    @app.route("/")
    def index():
        pass
    
    url_for("index")
    
    • 使用蓝图时, 使用 蓝图名.视图函数名 字符串得到URL
    
    login_blue = Blueprint("login", __name__, url_prefix="/login")
    
    @login_blue.route('/')
    def index():
        return 'login'
    
    url_for("login.index") # 得到 /login/
    

    到此结  DragonFangQy 2018.6.30

    相关文章

      网友评论

        本文标题:Flask初探五( Blueprint / url_for /

        本文链接:https://www.haomeiwen.com/subject/hkotuftx.html