在一个Flask 应用项目中,如果业务视图过多,可否将以某种方式划分出的业务单元单独维护,将每个单元用到的视图、静态文件、模板文件等独立分开?
例如从业务角度上,可将整个应用划分为用户模块单元、商品模块单元、订单模块单元,如何分别开发这些不同单元,并最终整合到一个项目应用中。
1、蓝图介绍
1.1 蓝图介绍
在Flask中,使用蓝图Blueprint来分模块组织管理。【蓝图相当于Django中的子应用】
蓝图实际可以理解为是一个存储一组视图方法的容器对象,其具有如下特点:
- 一个应用可以具有多个Blueprint
- 可以将一个Blueprint注册到任何一个未使用的URL下比如 “/user”、“/goods”
- Blueprint可以单独具有自己的模板、静态文件或者其它的通用操作方法,它并不是必须要实现应用的视图和函数的
- 在一个应用初始化时,就应该要注册需要使用的Blueprint
注意:
但是一个Blueprint并不是一个完整的应用,它不能独立于应用运行,而必须要注册到某一个应用中。
1.2 蓝图简单使用方式
使用蓝图可以分为三个步骤
1. 创建一个蓝图对象【user是蓝图的名字】
user_bp=Blueprint('user',__name__)
2. 在这个蓝图对象上进行操作,注册路由,指定静态文件夹,注册模版过滤器
@user_bp.route('/')
def user_profile():
return 'user_profile'
3. 在应用对象上注册这个蓝图对象
app.register_blueprint(user_bp)
1.3 单文件蓝图
可以将创建蓝图对象与定义视图放到一个文件中 。
示例代码:
from flask import Flask, Blueprint
app = Flask(__name__)
# 创建蓝图对象
user_bp = Blueprint('user', __name__)
@user_bp.route('/')
def user_profile():
return 'user_profile'
# 注册蓝图
app.register_blueprint(user_bp)
if __name__ == '__main__':
app.run()
运行效果:
1.4 指定蓝图的url前缀
在应用中注册蓝图时使用url_prefix
参数指定
app.register_blueprint(user_bp, url_prefix='/user')
app.register_blueprint(goods_bp, url_prefix='/goods')
注意:在注册app.register_blueprint(book_bp, url_prefix="/book")或定义蓝图bp = Blueprint("book", __name__, url_prefix="/book")时,这两个地方只能一个地方定义url_prefix,若定义路径相同不受影响,若两个地方定义的不同的话,以app.register_blueprint(book_bp, url_prefix="/book")为准。
注意:两个地方同时指定url_prefix时不是路由的拼接。
示例代码1: 【只有在注册蓝图时写url_prefix】
from flask import Flask, Blueprint
app = Flask(__name__)
# 创建蓝图对象
user_bp = Blueprint('user', __name__)
@user_bp.route('/')
def user_profile():
return 'user_profile'
# 注册蓝图
app.register_blueprint(user_bp, url_prefix='/user')
if __name__ == '__main__':
app.run()
运行效果:
示例代码2:【在注册蓝图和定义蓝图时写同一个url_prefix】
from flask import Flask, Blueprint
app = Flask(__name__)
# 创建蓝图对象
user_bp = Blueprint('user', __name__, url_prefix='/user')
@user_bp.route('/')
def user_profile():
return 'user_profile'
# 注册蓝图
app.register_blueprint(user_bp, url_prefix='/user')
if __name__ == '__main__':
app.run()
运行结果:
示例代码3:【在注册蓝图和定义蓝图时写不一样的url_prefix】
from flask import Flask, Blueprint
app = Flask(__name__)
# 创建蓝图对象
user_bp = Blueprint('user', __name__, url_prefix='/user_obj')
@user_bp.route('/')
def user_profile():
return 'user_profile'
# 注册蓝图
app.register_blueprint(user_bp, url_prefix='/user')
if __name__ == '__main__':
app.run()
运行结果:
2、蓝图的目录结构
为了让项目代码更加清晰,可以通过将代码分在不同的文件里进行管理。
2.1 根据功能模块
对于一个打算包含多个文件的蓝图,通常将创建蓝图对象放到Python包的 __init__.py 文件中。
--------- project # 工程目录
|------ main.py # 启动文件
|------ user #用户蓝图
| |--- __init__.py # 此处创建蓝图对象
| |--- passport.py
| |--- profile.py
| |--- ...
|
|------ goods # 商品蓝图
| |--- __init__.py
| |--- ...
|...
2.2 根据技术模块
--------- project # 工程目录
|------ main.py # 启动文件
|------ view #用户蓝图
| |--- user.py # 此处创建蓝图对象
| |--- item.py
| |--- view.py
| |--- ...
|...
2.3 实例代码
示例代码:
__init__.py
from flask import Blueprint
goods_bp = Blueprint('goods', __name__)
from . import views
views.py
from . import goods_bp
@goods_bp.route('/goods')
def get_goods():
return 'get goods!'
main.py:
from flask import Flask, Blueprint
app = Flask(__name__)
# 创建蓝图对象
user_bp = Blueprint('user', __name__)
@user_bp.route('/')
def user_profile():
return 'user_profile'
# 注册蓝图
# app.register_blueprint(user_bp)
app.register_blueprint(user_bp, url_prefix='/user')
from goods import goods_bp
app.register_blueprint(goods_bp)
if __name__ == '__main__':
app.run()
运行效果:
3、蓝图中模板文件
3.1 蓝图内部模板目录
蓝图对象默认的模板目录为系统的模版目录,可以在创建蓝图对象时使用 template_folder 关键字参数设置模板目录。
admin = Blueprint('admin',__name__,template_folder='my_templates')
bp = Blueprint('admin',__name__,url_prefix='/admin',template_folder='templates')
模板文件和静态文件有点区别,以上代码写完以后,如果你渲染一个模板return render_template('user.html'),Flask默认会去项目根目录下的templates文件夹中查找user.html文件,如果找到了就直接返回,如果没有找到,才会去蓝图文件所在的目录下的templates文件夹中寻找。
注意:
如果项目中的templates文件夹中有相应的模版文件,就直接使用了。
如果项目中的templates文件夹中没有相应的模版文件,那么就到在定义蓝图的时候指定的路径中寻找。
并且蓝图中指定的路径可以为相对路径,相对的是当前这个蓝图文件所在的目录
注意:
- 个性化coder喜欢在【创建蓝图对象的时候】 指定 模版文件的查找路径,如下
- news_bp =Blueprint('news',__name__,url_prefix='/news',template_folder='news_page')
- 只有确定templates目录下没有对应的 html文件名的时候,才会去蓝图文件指定的目录下查找,指定才会生效
- 若templates目录下,有一个与蓝图文件指定的目录下同名的一个 html文件时,优先走templates目录下的东西
4、蓝图中静态文件
和应用对象不同,蓝图对象创建时不会默认注册静态目录的路由。需要在创建时指定 static_folder 参数。
下面的示例将蓝图所在目录下的static_admin目录设置为静态目录
admin = Blueprint("admin",__name__,static_folder='static_admin')
app.register_blueprint(admin,url_prefix='/admin')
现在就可以使用/admin/static_admin/<filename>
访问static_admin
目录下的静态文件了。
也可通过static_url_path
改变访问路径
admin = Blueprint("admin",__name__,static_folder='static_admin',static_url_path='/lib')
app.register_blueprint(admin,url_prefix='/admin')
static_folder可以是相对路径(相对蓝图文件所在的目录),也可以是绝对路径。在配置完蓝图后,还有一个需要注意的地方是如何在模板中引用静态文件。在模板中引用蓝图,应该要使用蓝图名+.+static来引用,如下所示:
<link href="{{ url_for('user.static',filename='base.css') }}">
注意:
查找方式1:查找静态文件时,正常情况下,会以static为根目录进行查找
<link href="{{ url_for('user.static',filename='news_list.css') }}" rel="stylesheet" type="text/css">
查找方式2:查找静态文件时,非正常情况下,需要用url_for('蓝图的名字.static'),然后会去蓝图对象在创建时指定的静态文件夹目录下 去查找静态文件
user_bp = Blueprint('user',__name__,url_prefix='/user',static_folder='user_statics')
<link href="{{ url_for('user.static',filename='user.css') }}" rel="stylesheet" type="text/css">
5、蓝图url_for函数
如果使用蓝图,那么以后想要反转蓝图中的视图函数为url,就应该在使用url_for的时候指定这个蓝图名字。
app类中、模版中、同一个蓝图类中都是如此。否则就找不到这个endpoint
用url_for生成蓝图的url,使用的格式是:蓝图名称+.+视图函数名称。比如要获取user这个蓝图下的index视图函数的url,应该采用以下方式:
url_for('user.index')
其中这个蓝图名称是在创建蓝图的时候,传入的第一个参数。bp = Blueprint('user',__name__,url_prefix='/user',template_folder='templates')。
html文件中写法:
<a href="{{ url_for('user.user_list')}}">新闻列表 OK写法</a>
{# <a href="{{ url_for('user_list')}}">新闻列表 no Ok写法</a>#}
蓝图使用url_for一个案例:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<div>
图书列表URL:{{ book_url }} <br>
用户列表URL:{{ user_url }}
</div>
</body>
</html>
main.py
from flask import Flask, render_template, url_for
from apps.book import bp as book_bp
from apps.user import bp as user_bp
app = Flask(__name__)
app.register_blueprint(book_bp, url_prefix="/book/")
app.register_blueprint(user_bp)
@app.route('/')
def index():
data = {
"book_url": url_for('book.book_list'),
"user_url": url_for('user.user_list')
}
return render_template('index.html', **data)
if __name__ == '__main__':
app.run()
book.py
from flask import Blueprint
bp = Blueprint("book", __name__, url_prefix="/book")
@bp.route('/list')
def book_list():
return "图书列表"
user.py
from flask import Blueprint
bp = Blueprint("user", __name__, url_prefix="/user")
@bp.route('/list')
def user_list():
return "用户列表"
运行效果:
6、蓝图中子域名实现
6.1 蓝图实现子域名
1. 使用蓝图技术。
2. 在创建蓝图对象的时候,需要传递一个 subdomain 参数,来指定这个子域名的前缀。
cms_bp=Blueprint('cms',__name__,subdomain='cms')
3. 需要在主app文件中,需要配置app.config的SERVER_NAME参数。例如:
app.config['SERVER_NAME']='sxt.com:5000'
4. 在windows: C:\Windows\System32\drivers\etc 下,找到hosts文件,然后添加域名与本机的映射。Linux: /etc/hosts
域名和子域名都需要做映射:
127.0.0.1 sxt.com
127.0.0.1 python.sxt.com
注意:
- ip地址不能有子域名
- localhost也不能有子域名
6.2 实例演示
配置hosts文件:
示例代码:
from flask import Flask, Blueprint
app = Flask(__name__)
app.config['SERVER_NAME'] = 'flask_test.com:5000'
# 创建蓝图对象
user_bp = Blueprint('user', __name__, url_prefix='/user', subdomain='user')
@app.route('/')
def index():
return "返回首页!"
@user_bp.route('/')
def user_index():
return '返回用户主页!'
# 注册蓝图
app.register_blueprint(user_bp)
if __name__ == '__main__':
app.run()
运行结果: