flask blueprint

为什么使用蓝图?

Flask 中蓝图有以下用途:

  • 把一个应用分解为一套蓝图。这是针对大型应用的理想方案:一个项目可以实例化 一个应用,初始化多个扩展,并注册许多蓝图。

  • 在一个应用的 URL 前缀和(或)子域上注册一个蓝图。 URL 前缀和(或)子域的 参数成为蓝图中所有视图的通用视图参数(缺省情况下)。

  • 使用不同的 URL 规则在应用中多次注册蓝图。

  • 通过蓝图提供模板过滤器、静态文件、模板和其他工具。蓝图不必执行应用或视图 函数。

  • 当初始化一个 Flask 扩展时,为以上任意一种用途注册一个蓝图。

Flask 中的蓝图不是一个可插拨的应用,因为它不是一个真正的应用,而是一套可以 注册在应用中的操作,并且可以注册多次。那么为什么不使用多个应用对象呢?可以 使用多个应用对象(参见 应用调度 ),但是这样会导致每个 应用都使用自己独立的配置,且只能在 WSGI 层中管理应用。

而如果使用蓝图,那么应用会在 Flask 层中进行管理,共享配置,通过注册按需改 变应用对象。蓝图的缺点是一旦应用被创建后,只有销毁整个应用对象才能注销蓝图。

蓝图的概念

蓝图的基本概念是:在蓝图被注册到应用之后,所要执行的操作的集合。当分配请求 时, Flask 会把蓝图和视图函数关联起来,并生成两个端点之前的 URL 。

第一个蓝图

以下是一个最基本的蓝图示例。在这里,我们将使用蓝图来简单地渲染静态模板:

from flask import Blueprint, render_template, abort
from jinja2 import TemplateNotFound

simple_page = Blueprint('simple_page', __name__,
                        template_folder='templates')

@simple_page.route('/', defaults={'page': 'index'})
@simple_page.route('/<page>')
def show(page):
    try:
        return render_template(f'pages/{page}.html')
    except TemplateNotFound:
        abort(404)

注册蓝图

from flask import Flask
from yourapplication.simple_page import simple_page

app = Flask(__name__)
app.register_blueprint(simple_page)

以下是注册蓝图后形成的规则:

app.url_map
Map([<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
 <Rule '/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
 <Rule '/' (HEAD, OPTIONS, GET) -> simple_page.show>])

第一条很明显,是来自于应用本身的用于静态文件的。后面两条是用于蓝图 simple_pageshow 函数的。你可以看到,它们的前缀都是蓝图的名称,并 且使用一个点( . )来分隔。

蓝图还可以挂接到不同的位置:

app.register_blueprint(simple_page, url_prefix='/pages')

这样就会形成如下规则:

app.url_map
Map([<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
 <Rule '/pages/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
 <Rule '/pages/' (HEAD, OPTIONS, GET) -> simple_page.show>])

总之,你可以多次注册蓝图,但是不一定每个蓝图都能正确响应。是否能够多次注册 实际上取决于你的蓝图是如何编写的,是否能根据不同的位置做出正确的响应。

嵌套蓝图

把一个蓝图注册在另一个蓝图上是可行的。

parent = Blueprint('parent', __name__, url_prefix='/parent')
child = Blueprint('child', __name__, url_prefix='/child')
parent.register_blueprint(child)
app.register_blueprint(parent)

蓝图会把父蓝图的名称作为其前缀,子 URL 也会把父 URL 作为前缀。

url_for('parent.child.create')
/parent/child/create

蓝图指定的请求前函数等会为子蓝图触发。如果子蓝图没有可以处理异常的出错 处理器,那么会尝试父蓝图的出错处理。

静态文件

蓝图的第三个参数是 static_folder 。这个参数用以指定蓝图的静态文件所在的 文件夹,它可以是一个绝对路径也可以是相对路径。

admin = Blueprint('admin', __name__, static_folder='static')

缺省情况下,路径最右端的部分是在 URL 中暴露的部分。这可以通过 static_url_path 来改变。因为上例中的文件夹为名称是 static ,那么 URL 应该是蓝图的 url_prefix 加上 /static 。 如果蓝图注册前缀为 /admin ,那么静态文件 URL 就是 /admin/static

端点的名称是 blueprint_name.static 。你可以像对待应用中的文件夹一样 使用 url_for() 来生成其 URL:

url_for('admin.static', filename='style.css')

但是,如果蓝图没有 url_prefix ,那么不可能访问蓝图的静态文件夹。 这是因为在这种情况下,URL应该是 / static ,而应用程序的 / static 路线优先。与模板文件夹不同,如果文件不存在于应用静态文件夹中,那么不会 搜索蓝图静态文件夹。

模板

如果你想使用蓝图来暴露模板,那么可以使用 Blueprinttemplate_folder 参数:

admin = Blueprint('admin', __name__, template_folder='templates')

案例

文件架构

apps
    --user
        --__init__.py
        --model.py
        --view.py
    --__init__.py
     --setting.py
static
templates
    --base.html
    --user
        --register.html
        --login.html
        --show.html
app.py

 app.py

from apps import create_app
app = create_app()
if __name__=='__main__':
    app.run()

apps/__init__.py

from flask import Flask
from . import setting
from apps.user import user_bp

def create_app():
    app = Flask(__name__,template_folder='../templates',static_folder='../static') #指定蓝图的模版、静态文件
    app.config.from_object(setting)
    #蓝图
    app.register_blueprint(user_bp) #注册蓝图
    print(app.url_map)
    return app

apps/setting.py

# 配置文件
ENV = 'development'
DEBUG = True

apps/user/__init__.py

from flask import Blueprint
user_bp=Blueprint('users',__name__)创建蓝图
from .view import *

apps/user/model.py

class User:
    def __init__(self,username,password,phone=None):
        self.username = username
        self.password = password
        self.phone =phone
    def __str__(self):
        return self.username

apps/user/view.py

from flask import request,render_template,redirect
from apps.user.model import User
from apps.user import user_bp 

users = []
@user_bp.route('/',endpoint='usercenter')
def user_center():
    return render_template('user/show.html',users=users)

@user_bp.route('/register',methods = ['GET','POST'])
def register():
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        repassword = request.form.get('repassword')
        phone = request.form.get('phone')
        if password == repassword:
            for user in users:
                if user.username == username:
                    return render_template('user/register.html',msg='用户名已存在')
            user = User(username, password, phone)
            users.append(user)
            print(users)
            return redirect('/')

    return render_template('user/register.html')

@user_bp.route('/login',methods = ['GET','POST'])
def login():
    return 'user-login'

@user_bp.route('/logout',methods = ['GET','POST'])
def logout():
    return 'user-logout'

@user_bp.route('/del')
def del_user():
    username = request.args.get('username')
    for user in users:
        if user.username == username:
            users.remove(user)
            return '删除成功'
    else:
        return '删除失败'

template/base.html

<!doctype html>
<html lang="en">
<head>
    <title>{% block title %}{% endblock %}</title>
    {% block mycss %}{% endblock %}
<style>
        #head{
            height: 50px;
            background-color: bisque;
        }
        #head ul{
            list-style: none;
            height: 50px;
        }
        #head ul li{
            float: left;
            width: 100px;
            text-align: center;
            font-size: 18px;
            line-height: 50px;
        }
        middle{
            height: 900px;
            background-color: darkseagreen;
        }
        foot{
            height: 50px;
            line-height: 50px;
        }
    </style>

</head>
<body>
<div id="head">
    <ul>
        <li>首页</li>
        <li>秒杀</li>
        <li>超市</li>
        <li>图书</li>
        <li>会员</li>
    </ul>
</div>

<div id="center">
    {% block center %}
    
    {% endblock %}
</div>

<div id="foot">
    {% block foot %}
    
    {% endblock %}
</div>

{% block myjs %}{% endblock %}
</body>
</html>

template/user/register.html

{% extends 'base.html' %}
{% block title %}
    用户注册
{% endblock %}

{% block center %}
    <p>{{ msg }}</p>
    <form action="/register" method="post">
        <p><input type="text" name="username" placeholder="用户名"></p>
        <p><input type="password" name="password" placeholder="密码"></p>
        <p><input type="password" name="repassword" placeholder="确认密码"></p>
        <p><input type="number" name="phone" placeholder="手机号码"></p>
        <p><input type="submit" value="用户注册"></p>
    </form>
{% endblock %}


{% block foot %}

{% endblock %}

template/user/show.html

{% extends 'base.html' %}
{% block center %}

<h1>用户信息</h1>
<span>当前用户人数:{{ users|length }}</span>

<table>
    {% for user in users %}
        <tr>
            <td>{{ loop.index }}</td>
            <td>{{ user.username }}</td>
            <td>{{ user.password }}</td>
            <td>{{ user.phone }}</td>
            <td><a href="">修改</a><a href="javascript:;" onclick="del('{{ user.username }}')">删除</a></td>
        </tr>
    {% endfor %}

</table>

{% endblock %}

{% block myjs %}
    <script>
        function del(username){
        //consul.log(username)
            location.href = '/del?username=' +username
    }
    </script>
{% endblock %}

参考:

https://dormousehole.readthedocs.io/en/latest/tutorial/views.html?highlight=%E8%93%9D%E5%9B%BE

 

 

 

posted @ 2021-09-17 18:04  fat_girl_spring  阅读(88)  评论(0编辑  收藏  举报