博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
flask中的蓝图
阅读量:6581 次
发布时间:2019-06-24

本文共 7848 字,大约阅读时间需要 26 分钟。

hot3.png

个人认为,引入蓝图,主要将大型的应用,分解成一个个小的部分,这些小的部分就是蓝图。还可以在一个应用的URL前缀、子域上注册一个蓝图;使用不同的URL规则在应用中多次注册蓝图;并且还可以通过蓝图提供模版过滤器、静态文件、模板和其他工具。使用蓝图可以使应用搬到Flask层面中进行管理。

蓝图架构:分为功能式架构和分区式架构。

功能式架构:

按照每部分代码的功能来组织你的应用。所有模版放到同一个文件夹中,静态文件放在另一个文件夹中,而视图放在第三个文件夹中。

yourapp/

        __init__.py

        static/

        templates/

                home/

                control_panel/

                admin/

        views/

                __init__.py

                home.py

                control_panel.py

                admin.py

       models.py

除了yourapp/views/__init__.py,在yourapp/views/文件夹中的每一个.py文件都是一个蓝图。在yourapp/init.py中,我们将加载这些蓝图并在我们的Flask()对象中注册它们。

分区式架构:

在分区式架构中,按照每一部分所属的蓝图来组织你的应用。管理面板的所有的模板,视图和静态文件放在一个文件夹中,用户控制面板则放在另一个文件夹中。

yourapp/

        __init__.py

       admin/

                __init__.py

               views.py

               static/

               templates/

       home/

               __init__.py

              views.py

              static/

              templates/

       control_panel/

             __init__.py

             views.py

             static/

             templates/

       models.py

在像上面列举的分区式结构,每一个yourapp/之下的文件夹都是一个独立的蓝图。所有的蓝图通过顶级的__init__.py注册到Flask()中。

选择分区式还是功能式?

如果你的应用是由独立的,仅仅共享模型和配置的各组建组成,分区式将是个好选择。一个例子是允许用户建立网站的SaaS应用。你将会有独立的蓝图用于主页,控制面板,用户网站,和高亮面板。这些组建有着完全不同的静态文件和布局。如果你想要将你的蓝图提取成插件,或用之于别的项目,一个分区式架构将是正确的选择。另一方面,如果你的应用的组件之间的联系较为紧密,使用功能式架构会更好。

蓝图基本用法:

让我们看看来自Facebook例子的一个蓝图的代码:

facebook/views/profile.py

from flask import Blueprint, render_templateprofile = Blueprint('profile', __name__)@profile.route('/
')def timeline(user_url_slug):    # 做些处理    return render_template('profile/timeline.html')@profile.route('/
/photos')def photos(user_url_slug):    # 做些处理    return render_template('profile/photos.html')@profile.route('/
/about')def about(user_url_slug):    # 做些处理    return render_template('profile/about.html')

要想创建一个蓝图对象,你需要importBlueprint()类并用参数nameimport_name初始化。通常用__name__,一个表示当前模块的特殊的Python变量,作为import_name的取值。

假如使用分区式架构,你得告诉Flask某个蓝图是有着自己的模板和静态文件夹的。下面是这种情况下我们的定义大概的样子:

profile = Blueprint('profile', __name__,                    template_folder='templates',                    static_folder='static')

现在我们已经定义好了蓝图。是时候向Flask app注册它了(采用register_blueprint函数)。

facebook/init.py

from flask import Flaskfrom .views.profile import profileapp = Flask(__name__)app.register_blueprint(profile)

现在在fackbook/views/profile.py中定义的路径(比如/<user_url_slug>)会被注册到应用中,就像是被通过@app.route()定义的。

继续看Facebook的例子,注意到所有的个人信息路由都以<user_url_slug>开头并把它传递给视图函数。我们想要用户通过类似的URL访问个人信息。通过给所有的蓝图的路由定义一个动态前缀,我们可以结束这种单调的重复。

蓝图允许我们定义静态的或动态的前缀。举个例子,我们可以告诉Flask蓝图中所有的路由应该以/profile作为前缀(在Blueprint函数中的参数url_prefix='/profile');这样是一个静态前缀。在Fackbook这个例子中,前缀取决于用户浏览的是谁的个人信息。他们在URL对应片段中输入的文本将决定我们输出的视图;这样是一个动态前缀。

我们可以选择何时定义我们的前缀。我们可以在下列两个时机中选择一个定义前缀:当我们实例化Blueprint()类的时候,或当我们在app.register_blueprint()中注册的时候。

下面我们在实例化的时候设置URL前缀:

facebook/views/profile.py

from flask import Blueprint, render_templateprofile = Blueprint('profile', __name__, url_prefix='/
')# [...]

下面我们在注册的时候设置URL前缀:

facebook/init.py

from flask import Flaskfrom .views.profile import profileapp = Flask(__name__)app.register_blueprint(profile, url_prefix='/
')

尽管这两种方式在技术上没有区别,最好还是在注册的同时定义前缀。这使得前缀的定义可以集中到顶级目录中。因此,我推荐使用url_prefix

我们可以在前缀中使用转换器(converters),就像调用route()一样。同样也可以使用我们定义过的任意自定义转换器。通过这样做,我们可以 自动处理在蓝图前缀中传递过来的值。在这个例子中,我们将根据URL片段获取用户类并传递到我们的profile蓝图中。我们将通过一个名为url_value_preprocessor()装饰器来做到这一点。

facebook/views/profile.py

from flask import Blueprint, render_template, gfrom ..models import User# The prefix is defined in facebook/__init__.py.profile = Blueprint('profile', __name__)@profile.url_value_preprocessordef get_profile_owner(endpoint, values):    query = User.query.filter_by(url_slug=values.pop('user_url_slug'))    g.profile_owner = query.first_or_404()@profile.route('/')def timeline():    return render_template('profile/timeline.html')@profile.route('/photos')def photos():    return render_template('profile/photos.html')@profile.route('/about')def about():    return render_template('profile/about.html')

我们使用g对象来储存个人信息的拥有者,而g可以用于Jinja2模板上下文。这意味着在这个简单的例子中,我们仅仅需要渲染模板,需要的信息就能在模板中获取。

facebook/templates/profile/photos.html

{% extends "profile/layout.html" %}{% for photo in g.profile_owner.photos.all() %}    
{% endfor %}

使用一个动态子域:

今天,许多SaaS应用提供用户一个子域名来访问他们的软件。举个例子,Harvest,是一个针对顾问的日程管理软件,它在yourname.harvestapp.com给你提供了一个控制面板。下面我将展示在Flask中如何像这样自动生成一个子域名。

在这一节,我将使用一个允许用户创建自己的网站的应用作为例子。假设我们的应用有三个蓝图分别针对以下的部分:用户注册的主页面,可用于建立自己的网站的用户管理面板,用户的网站。考虑到这三个部分相对独立,我们将用分区式结构组织起来。

sitemaker/    __init__.py    home/        __init__.py        views.py        templates/            home/        static/            home/    dash/        __init__.py        views.py        templates/            dash/        static/            dash/    site/        __init__.py        views.py        templates/            site/        static/            site/    models.py
url 蓝图目录 作用
sitemaker.com/ sitemaker/home 一个普通的蓝图。包括用于index.htmlabout.htmlpricing.html的视图,模板和静态文件。
bigdaddy.sitemaker.com sitemaker/site 这个蓝图使用了动态子域名,并包括了用户网站的一些元素。等下我们来看看用于实现这个蓝图的一些代码。
bigdaddy.sitemaker.com/admin sitemaker/dash 这个蓝图将使用一个动态子域名和一个URL前缀,把这一节的技术和上一节的结合起来。

定义动态子域名的方式和定义URL前缀一样。同样的,我们可以选择在蓝图文件夹中,或在顶级目录的init.py中定义它。这一次,我们还是在sitemaker/init.py中放置所有的定义。

sitemaker/init.py

from flask import Flaskfrom .site import siteapp = Flask(__name__)app.register_blueprint(site, subdomain='
')

既然我们用的是分区式架构,蓝图将在sitemaker/site/init.py定义。

sitemaker/site/initpy

from flask import Blueprint# 注意首字母大写的Site和全小写的site是两个完全不同的变量。Site是一个模块,而site是一个蓝图。from ..models import Sitesite = Blueprint('site', __name__)@site.url_value_preprocessordef get_site(endpoint, values):    query = Site.query.filter_by(subdomain=values.pop('site_subdomain'))# 在定义site后才import views。视图模块需要import 'site',所以我们需要确保在import views之前定义site。    g.site = query.first_or_404()import .views

现在我们已经从数据库中获取可以向请求子域名的用户展示的站点信息了。

为了使Flask能够支持子域名,你需要修改配置变量SERVER_NAME

config.py

SERVER_NAME = 'sitemaker.com'

注意几分钟之前,当我正在打这一章的草稿时,聊天室中某人求助称他们的子域名能够在开发环境下正常工作,但在生产环境下就会失败。我问他们是否配置了SERVER_NAME,结果发现他们只在开发环境中配置了这个变量。在生产环境中设置这个变量解决了他们的问题。从这里可以看到我(imrobert)和aplavin之间的对话:

注意你可以同时设置一个子域名和URL前缀。想一下使用上面的表格的URL结构,我们要怎样来配置sitemaker/dash

使用蓝图重构小型应用

我打算通过一个简单的例子来展示用蓝图重写一个应用的几个步骤。我们将从一个典型的Flask应用起步,然后重构它。

config.txtrequirements.txtrun.pygnizama/  __init__.py  views.py  models.py  templates/  static/tests/

views.py文件已经膨胀到10,000行代码了。重构的工作被一推再推,到现在已经无路可退。这个文件包括了我们的网站的所有的视图,比如主页,用户面板,管理员面板,API和公司博客。

Step 1:分区式还是功能式?

这个应用由关联较小的各部分构成。模板和静态文件不太可能在蓝图间共享,所以我们将使用分区式结构。

Step 2:分而治之

注意在你对你的应用大刀阔斧之前,把一切提交到版本控制。你不会接受对任何有用的东西的意外删除。

接下来我们将继续前进,为我们的新应用创建目录树。从为每一个蓝图创建一个目录开始吧。然后整体复制views.pystatic/templates/到每一个蓝图文件夹。接着你可以从顶级目录删除掉它们了。

config.txtrequirements.txtrun.pygnizama/  __init__.py  home/    views.py    static/    templates/  dash/    views.py    static/    templates/  admin/    views.py    static/    templates/  api/    views.py    static/    templates/  blog/    views.py    static/    templates/  models.pytests/

Step 3:大扫除

现在我们可以到每一个蓝图中,移除无关的视图,静态文件和模板。你在这一阶段的处境很大程度上取决于一开始你是怎么组织你的应用的。

最终结果应该是:每个蓝图有一个views.py包括了蓝图里的所有视图,没有两个蓝图对同一个路由定义了视图;每一个templates/文件夹应该只包括该蓝图所需的模板;每一个static/文件夹应该只包括该蓝图所需的静态文件。

注意趁此机会消除所有不必要的import。很容易忽略掉他们的存在,但他们会拥塞你的代码,甚至拖慢你的应用。

Step 4:蓝图

在这一部分我们把文件夹转换成蓝图。关键在于init.py文件。作为开始,让我们看一下API蓝图的定义。

gnizama/api/init.py

from flask import Blueprintapi = Blueprint(    'site',    __name__,    template_folder='templates',    static_folder='static')import .views

接着我们可以在gnizama的顶级目录下的init.py中注册这个蓝图。

gnizama/init.py

from flask import Flaskfrom .api import apiapp = Flask(__name__)# 在api.gnizama.com中添加API蓝图app.register_blueprint(api, subdomain='api')

确保路由现在是在蓝图中注册的,而不是在app对象。下面是在我们重构应用之前,一个在gnizama/views.py的API路由可能的样子。

gnizama/views.py

from . import app@app.route('/search', subdomain='api')def api_search():    pass

在蓝图中它看上去像这样:

gnizama/api/views.py

from . import api@api.route('/search')def search():    pass

Step 5:大功告成

现在我们的应用已经比只有单个臃肿的views.py的时候更加模块化了。

总结

  • 一个蓝图包括了可以作为独立应用的视图,模板,静态文件和其他插件。

  • 蓝图是组织你的应用的好办法。

  • 在分区式架构下,每个蓝图对应你的应用的一个部分。

  • 在功能式架构下,每个蓝图就只是视图的集合。所有的模板和静态文件都放在一块。

  • 要使用蓝图,你需要定义它,并在应用中用Flask.register_blueprint()注册它。

  • 你可以给一个蓝图中的所有路由定义一个动态URL前缀。

  • 你也可以给蓝图中的所有路由定义一个动态子域名。

  • 仅需五步走,你可以用蓝图重构一个应用。

 

转载于:https://my.oschina.net/935572630/blog/371445

你可能感兴趣的文章
Command './js-ant' failed to execute
查看>>
阿里云NFS NAS数据保护实战
查看>>
Spring cloud配置客户端
查看>>
产品研发项目管理软件哪个好?
查看>>
【阿里云北京峰会】一图看懂机器学习PAI如何帮助企业应用智能化升级
查看>>
ansible playbook使用总结
查看>>
Android API中文文档(111) —— MailTo
查看>>
Linux 中如何卸载已安装的软件
查看>>
thinkphp 3.2 增加每页显示条数
查看>>
oracle日常简单数据备份与还原
查看>>
我的友情链接
查看>>
黑马程序员__反射总结
查看>>
Scala学习笔记(5)-类和方法
查看>>
Quartz原理
查看>>
完全卸载oracle|oracle卸载|彻底卸载oracle
查看>>
垃圾收集基础
查看>>
Docker安装及基本命令
查看>>
控制namenode检查点发生的频率
查看>>
Linux存储挂载后,无法正常卸载的解决方法
查看>>
2、递归遍历文件夹下每一个文件
查看>>