烧瓶中的嵌套蓝图?

Jar*_*edL 13 python design-patterns flask

我仍然是Flask的新手,因此可能有一种明显的方法可以实现这一目标,但到目前为止我还没有能够从文档中找到它.我的应用程序分为几个主要是不同的部分,分享用户/会话/安全性和基本模板等所有内容,但大多数情况下不会进行很多交互,应该在不同的路径下进行路由/part1/....我认为这正是蓝图的用武之地.但是如果我需要在蓝图下进一步分组路由和逻辑呢?

例如,我有blueprint1,url_prefix='/blueprint1'也许在那之下我希望有一组视图围绕用户共享照片和其他用户评论它们.我想不出比这更好的做法:

# app/blueprints/blueprint1/__init__.py

blueprint1 = Blueprint('blueprint1', __name__, template_folder='blueprint1')

@blueprint1.route('/photos')
def photos_index():
    return render_template('photos/index.html')

@blueprint.route('/photos/<int:photo_id>')
def photos_show(photo_id):
    photo = get_a_photo_object(photo_id)
    return render_template('photos/show.html', photo=photo)

@blueprint.route('/photos', methods=['POST'])
def photos_post():
    ...
Run Code Online (Sandbox Code Playgroud)

这里的问题是所有与照片部分相关的视图blueprint1都位于"顶层",右侧可能有视频或音频或其他任何(命名为videos_index()......)的蓝图.有没有办法以更分层的方式对它们进行分组,比如模板如何在'blueprint1/photos'子目录下?当然,我可以将所有照片视图放在他们自己的模块中,以使它们分开组织,但是如果我想将父'blueprint1/photos'路径更改为其他内容呢?我确信我可以创建一个在相同根路径下对相关路由进行分组的函数或装饰器,但是我仍然必须使用photos_前缀命名所有函数并引用它们url_for('blueprint1.photos_show') 当Flask应用程序变大并且您需要将相似的部分组合在一起时,蓝图就好了,但是当蓝图本身变大时,您无法做同样的事情.

作为参考,在Laravel中,您可以Controller在视图是方法的类下对相关的"视图"进行分组.控制器可以驻留在分层名称空间中,例如app\Http\Controllers\Blueprint1\Photocontroller,路由可以组合在一起

Route::group(['prefix' => 'blueprint1'], function() {

    Route::group(['prefix' => 'photos'], function() {

        Route::get('/', ['as' => 'blueprint.photos.index', 'uses' => 'ModelApiController@index']);
        Route::post('/', ['as' => 'blueprint.photos.store', 'uses' => 'ModelApiController@store']);
        Route::get('/{id}', ['as' => 'blueprint.photos.get', 'uses' => 'ModelApiController@get'])
            ->where('id', '[0-9]+');

    });

});
Run Code Online (Sandbox Code Playgroud)

和路线可以得到像action('Blueprint1\PhotoController@index').

如果只有我可以制作照片蓝图,那么只是做blueprint1.register_blueprint(photos_blueprint, url_prefix='/photos')或类似,这些问题几乎可以解决.不幸的是,Flask似乎不支持这样的嵌套蓝图.有没有其他方法来处理这个问题?

小智 17

更新

\n

Flask 2 发布后支持嵌套蓝图。

\n

[开始:文档的一部分]

\n

嵌套蓝图

\n

可以将一个蓝图注册到另一个蓝图上。

\n
parent = Blueprint(\'parent\', __name__, url_prefix=\'/parent\')\nchild = Blueprint(\'child\', __name__, url_prefix=\'/child\')\nparent.register_blueprint(child)\napp.register_blueprint(parent)\n
Run Code Online (Sandbox Code Playgroud)\n

子蓝图将获取父\xe2\x80\x99s 名称作为其名称的前缀,并且子 URL 将使用父\xe2\x80\x99s URL 前缀作为前缀。

\n
url_for(\'parent.child.create\')\n/parent/child/create\n
Run Code Online (Sandbox Code Playgroud)\n

向父级注册的特定于蓝图的请求前函数等将为子级触发。如果子进程没有可以处理给定异常的错误处理程序,则将尝试父进程\xe2\x80\x99s。

\n

[结束:文档的一部分]

\n

来源:https ://flask.palletsprojects.com/en/2.0.x/blueprints/#nesting-blueprints

\n
\n

旧答案

\n

我的解决办法是我创建了一个名为ParentBP的类,它具有以下代码

\n
from typing import List\nfrom flask import Blueprint\n\nclass ParentBP(object):\n   name: str\n   url_prefix: str\n   subdomain: str\n   blueprints: List[Blueprint]\n\ndef __init__(self, name="", url_prefix="", subdomain="") -> None:\n    self.name = name\n    self.url_prefix = url_prefix\n    self.subdomain = subdomain\n    self.blueprints = []\n\ndef register_blueprint(self, bp: Blueprint) -> None:\n    bp.name = self.name + "-" + bp.name\n    bp.url_prefix = self.url_prefix + (bp.url_prefix or "")\n    if self.subdomain:\n        bp.subdomain = self.subdomain\n    self.blueprints.append(bp)\n
Run Code Online (Sandbox Code Playgroud)\n

所以你可以将其称为类似于下面的蓝图

\n
blueprint1 = Blueprint("blueprint1", __name__)\nblueprint2 = Blueprint("blueprint2", __name__, url_prefix="/bp2")\n\napi_v1 = ParentBP("api-v1", url_prefix="/api/v1")\napi_v1.register_blueprint(blueprint1)\napi_v1.register_blueprint(blueprint)\n
Run Code Online (Sandbox Code Playgroud)\n

为了使界面类似于正常将蓝图注册到 Flask 应用程序,我扩展了 Flask 类,如下所示

\n
class ExtendedFlask(Flask):\n\ndef register_blueprint(self, blueprint: Union[Blueprint, ParentBP], **options: Any) -> None:\n    if isinstance(blueprint, ParentBP):\n        for bp in blueprint.blueprints:\n            super().register_blueprint(bp, **options)\n    else:\n        return super().register_blueprint(blueprint, **options)\n
Run Code Online (Sandbox Code Playgroud)\n

现在您通常可以执行以下操作

\n
app = ExtendedFlask(__name__)\napp.register_blueprint(api_v1)\n
Run Code Online (Sandbox Code Playgroud)\n


dav*_*ism 8

不幸的是,嵌套蓝图不是Flask中的当前功能.你必须手动完成.您可以编写适合您特定情况的代码,但尚未向Flask添加一般解决方案.对问题跟踪器进行了一些讨论:


Abh*_*pta 6

我做了一个叫做NestedBlueprint破解它的课程。

class NestedBlueprint(object):
    def __init__(self, blueprint, prefix):
        super(NestedBlueprint, self).__init__()
        self.blueprint = blueprint
        self.prefix = '/' + prefix

    def route(self, rule, **options):
        rule = self.prefix + rule
        return self.blueprint.route(rule, **options)
Run Code Online (Sandbox Code Playgroud)

这是我的包含蓝图的基本文件: panel/__init__.py

from flask import Blueprint

panel_blueprint = Blueprint(PREFIX, __name__, url_prefix='/panel')

from . import customize
Run Code Online (Sandbox Code Playgroud)

这是包含嵌套蓝图的特定/嵌套文件: panel/customize.py

from rest.api.panel import panel_blueprint
from rest.api.util.nested_blueprint import NestedBlueprint

nested_blueprint = NestedBlueprint(panel_blueprint, 'customize')


@nested_blueprint.route('/test', methods=['GET'])
def test():
    return ':)'
Run Code Online (Sandbox Code Playgroud)

然后你可以这样调用:

$ curl http://localhost:5000/panel/customize/test
:)
Run Code Online (Sandbox Code Playgroud)