18.Flask视图

1 add_url_rule 与 app.route

1.1 add_url_rule

add_url_rule(rule, endpoint=None, view_func=None)

这个方法用来添加 url 与视图函数的映射。

如果没有填写 endpoint,那么默认会使用 view_func 的名字作为 endpoint

以后在使用 url_for 的时候,就要看在映射的时候有没有传递 endpoint 参数,如果传递了,那么就应该使用 endpoint 指定的字符串,如果没有传递,那么就应该使用 view_func 的名字。

def my_list():
	# 没有设置 endpoint 写 view_func 的名字
	# url = url_for("my_list")
	# 写了 endpoint,则使用 endpoint 的值,不能使用视图函数的名字
	url = url_for("list)
    return f"我是列表页<br>{url}"


app.add_url_rule('/my_list/', endpoint='list', view_func=my_list)

示例:

from flask import Flask, url_for

app = Flask(__name__)


# @app.route 底层就是使用的 add_url_rule
@app.route("/", endpoint="home")
def index():
    # url = url_for("show_me")
    url = url_for("show")
    url1 = url_for("home")
    return f"{url}<br>{url1}"


def show_me():
    return "这是介绍信息"


# endpoint 没有设置,url_for 中就写函数的名字,如果设置了,就写 endpoint 的值
app.add_url_rule("/show_me/", endpoint="show", view_func=show_me)

if __name__ == '__main__':
    app.run(debug=True)

1.2 app.route 原理剖析

app.route 装饰器底层,其实也是使用 add_url_rule 来实现 url 与视图函数映射的。

@setupmethod
def route(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]:
	def decorator(f: T_route) -> T_route:
		endpoint = options.pop("endpoint", None)
		self.add_url_rule(rule, endpoint, f, **options)
		return f

	return decorator

2 类视图

之前我们接触的视图都是函数,所以一般简称函数视图。

其实视图也可以基于类来实现,类视图的好处是支持继承。

但是类视图不能跟函数视图一样,通过 app.route 装饰器来注册 url。写完类视图还需要通过 app.add_url_rule(rule, endpoint, view_func) 来进行注册。

2.1 标准类视图使用步骤

  1. 标准类视图,必须继承自 flask.views.View
  2. 必须实现 dispatch_request 方法,以后请求过来后,都会执行这个方法。
    这个方法的返回值就相当于是之前的视图函数一样。也必须返回 Response 或者子类的对象,或者是字符串,或者是元组。
  3. 必须通过 app.add_url_rule(rule, endpoint, view_func) 来做 url 与视图的映射。view_func 这个参数,需要使用类视图下的 as_view 类方法类转换:类视图.as_view(视图名字)
  4. 如果指定了 endpoint,那么在使用 url_for 反转的时候就必须使用 endpoint 指定的那个值。如果没有指定 endpoint,那么就可以使用 as_view(视图名字) 中指定的视图名字来作为反转。
from flask import Flask, url_for
from flask.views import View

app = Flask(__name__)


@app.route("/")
def index():
    # url = url_for("my_view")
    url = url_for("my")
    return f"{url}"


class MyView(View):
    def dispatch_request(self):
        return "我是类视图内容!!!"


# 注册路由
app.add_url_rule("/my_view/", endpoint="my", view_func=MyView.as_view("my_view"))

# 用于测试,可以直接输出路由等数据,来测试路由是否有问题,不需要去浏览器进行查看页面确认
with app.test_request_context():
    print(url_for('my'))

if __name__ == '__main__':
    app.run(debug=True)

我们每次要测试视图是否可以正常反转,还需要打开浏览器访问页面,太麻烦了。使用 app.test_request_context 可以在其中编写测试代码,并且将结果直接打印到控制台。

# 用于测试,可以直接输出路由等数据,来测试路由是否有问题,不需要取浏览器进行查看页面确认
with app.test_request_context():
    print(url_for('my'))

3 类视图的好处

  1. 可以继承,把一些共性的东西抽取出来放到父视图中,子视图直接拿来用就可以了。
  2. 但是也不是说所有的视图都要使用类视图,这个要根据情况而定。视图函数用得最多。
from flask import Flask, jsonify
from flask.views import View

app = Flask(__name__)


@app.route("/")
def index():
    return "Hello"


# 需求:返回的结果都必须为 json 数据
class BaseView(View):
    def get_data(self):
        # 抛出没有实现错误
        raise NotImplementedError

    def dispatch_request(self):
        return jsonify(self.get_data())


class JsonView(BaseView):
    def get_data(self):
        return {"uname": "小龙女", "age": 18, "job": "掌门"}


class Json2View(BaseView):
    def get_data(self):
        return [
            {"uname": "赵敏", "age": 18},
            {"uname": "周芷若", "age": 20},
        ]


app.add_url_rule("/base/", view_func=BaseView.as_view("base"))
app.add_url_rule("/json/", view_func=JsonView.as_view("json"))
app.add_url_rule("/json2/", view_func=Json2View.as_view("json2"))

if __name__ == '__main__':
    app.run(debug=True)
  • 测试访问 http://127.0.0.1:5000/base/

  • 测试访问 http://127.0.0.1:5000/base/

  • 测试访问 http://127.0.0.1:5000/base/

4 类视图使用场景

上面两个页面中红框的部分可以视作广告语,该广告语由后台提供。以下方模拟实现的方式来的话,如果广告更新,那么 loginregister 视图传递的广告语信息都要进行更新。

现在还只是两个页面,更新比较简单,如果有很多页面都用到了这个广告语,那一个一个更新就太麻烦了。这时可以创建一个基类来存储广告语,让子类继承,这样更新广告语时只需要更新一处。

from flask import Flask, render_template
from flask.views import View

app = Flask(__name__)


@app.route('/')
def index():
    return "Hello"


class LoginView(View):
    def dispatch_request(self):
        return render_template("login.html", msg="百战老师好!!")


class RegisterView(View):
    def dispatch_request(self):
        return render_template("register.html", msg="百战老师好!!")


app.add_url_rule("/login/", view_func=LoginView.as_view("login"))
app.add_url_rule("/register/", view_func=RegisterView.as_view("register"))

if __name__ == '__main__':
    app.run(debug=True)

register.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
</head>

<body>
    <h2>Register</h2>
    <table>
        <tr>
            <td>用户名:</td>
            <td><input type="text" name="user"></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="password" name="pwd" id=""></td>
        </tr>
        <tr align="center">
            <td colspan="2"><input type="submit" value="注册"></td>
        </tr>
    </table>

    <hr>
    <p>公共的内容:{{ msg }}</p>
</body>

</html>

login.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>

<body>
    <h2>Login</h2>
    <table>
        <tr>
            <td>用户名:</td>
            <td><input type="text" name="user"></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="password" name="pwd" id=""></td>
        </tr>
        <tr align="center">
            <td colspan="2"><input type="submit" value="登录"></td>
        </tr>
    </table>

    <hr>
    <p>公共的内容:{{ msg }}</p>
</body>

</html>

创建基类来存储广告语:

from flask import Flask, render_template
from flask.views import View

app = Flask(__name__)


@app.route('/')
def index():
    return "Hello"


class BaseView(View):
    def __init__(self):
        self.msg = {
            "msg": "课程更新了"
        }


class LoginView(BaseView):
    def dispatch_request(self):
        # return render_template("login.html", msg="百战老师好!!")
        return render_template("login.html", msg=self.msg.get("msg"))


class RegisterView(BaseView):
    def dispatch_request(self):
        # return render_template("register.html", msg="百战老师好!!")
        return render_template("register.html", msg=self.msg.get("msg"))


app.add_url_rule("/login/", view_func=LoginView.as_view("login"))
app.add_url_rule("/register/", view_func=RegisterView.as_view("register"))

if __name__ == '__main__':
    app.run(debug=True)

还可以传递子类自己的信息:

class BaseView(View):
    def __init__(self):
        self.msg = {
            "msg": "课程更新了"
        }


class LoginView(BaseView):
    def dispatch_request(self):
        my_msg = "登录页面"
        
        # return render_template("login.html", msg="百战老师好!!")
        return render_template("login.html", msg=self.msg.get("msg"), my_msg=my_msg)


class RegisterView(BaseView):
    def dispatch_request(self):
        my_msg = "注册页面"
        
        # return render_template("register.html", msg="百战老师好!!")
        return render_template("register.html", msg=self.msg.get("msg"), my_msg=my_msg)

简化传递数据的方式:

class LoginView(BaseView):
    def dispatch_request(self):
        # my_msg = "登录页面"
        self.msg["my_msg"] = "登录页面"

        # return render_template("login.html", msg="百战老师好!!")
        # return render_template("login.html", msg=self.msg.get("main"), my_msg=my_msg)
        return render_template("register.html", **self.msg)


class RegisterView(BaseView):
    def dispatch_request(self):
        # my_msg = "注册页面"
        self.msg["my_msg"] = "注册页面"

        # return render_template("register.html", msg="百战老师好!!")
        # return render_template("register.html", msg=self.msg.get("main"), my_msg=my_msg)
        return render_template("register.html", **self.msg)

5 基于调度方法的类视图

  1. 基于方法的类视图,是根据请求的 method 来执行不同的方法的。如果用户是发送的 get 请求,那么将会执行这个类的 get 方法。
    如果用户发送的是 post 请求,那么将会执行这个类的 post 方法。其他的 method 类似,比如 deleteput

  2. 这种方式,可以让代码更加简洁。所有和 get 请求相关的代码都放在 get 方法中,所有和 post 请求相关的代码都放在 post 方法中。就不需要跟之前的函数一样,通过 request.method == 'GET'

5.1 案例

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>

<body>
    <h2>Login</h2>
    <form action="{{ url_for("login") }}" method="post">
        <table>
            <tr>
                <td>用户名:</td>
                <td><input type="text" name="uname"></td>
            </tr>
            <tr>
                <td>密码:</td>
                <td><input type="password" name="pwd" id=""></td>
            </tr>
            <tr align="center">
                <td colspan="2"><input type="submit" value="登录"></td>
            </tr>
        </table>
    </form>

    <span style="color:red;">
        {% if msg %}
            {{ msg }}
        {% endif %}
    </span>
</body>

</html>

我们实现一个登录页面请求,get 请求返回登录页面,post 请求在后台对浏览器提交的表单信息做判断,最后给出登录成功或失败的响应。

from flask import Flask, request, render_template

app = Flask(__name__)


@app.route("/")
def index():
    return "Hello"


@app.route("/login/", methods=["GET", "POST"])
def login():
    if request.method == "GET":
        return render_template("login.html")
    elif request.method == "POST":
        uname = request.form.get("uname")
        pwd = request.form.get("pwd")

        if uname == "xln" and pwd == "123":
            return f"{uname} 登录成功"
        else:
            return render_template("login.html", msg="用户名或密码错误")
    else:
        return "不支持的请求"


if __name__ == '__main__':
    app.run(debug=True)

改进 1:上面使用视图函数的方式可以实现我们的需求,但是每有一种请求方式就要写一个 if 判断,代码结构不够清晰。下面我们使用类视图来实现。

class LoginView(MethodView):
    def get(self):
        # 从数据库获取数据
        # sql
        # 执行
        # 从游标获取数据
        # 重新封装数据
        return render_template("login.html")

    def post(self):
        uname = request.form.get("uname")
        pwd = request.form.get("pwd")

        if uname == "xln" and pwd == "123":
            return f"{uname} 登录成功"
        else:
            # 从数据库获取数据
            # sql
            # 执行
            # 从游标获取数据
            # 重新封装数据
            return render_template("login.html", msg="用户名或密码错误")

改进 2post 请求判断登录失败后,返回的逻辑和 get() 方法一致,直接调用 get 方法即可。

class LoginView(MethodView):
    def get(self, msg=None):
        # 从数据库获取数据
        # sql
        # 执行
        # 从游标获取数据
        # 重新封装数据
        return render_template("login.html", msg=msg)

    def post(self):
        uname = request.form.get("uname")
        pwd = request.form.get("pwd")

        if uname == "xln" and pwd == "123":
            return f"{uname} 登录成功"
        else:
            return self.get(msg="用户名或密码错误")

改进 3:基于调度方法的类视图,通常 get() 方法处理 get 请求,post() 方法处理 post 请求,为了便于管理,不推荐 post 方法和 get 方法互相调用

class LoginView(MethodView):
    def __jump(self, msg=None):
        # 从数据库获取数据
        # sql
        # 执行
        # 从游标获取数据
        # 重新封装数据
        return render_template("login.html", msg=msg)

    def get(self):
        return self.__jump()

    def post(self):
        uname = request.form.get("uname")
        pwd = request.form.get("pwd")

        if uname == "xln" and pwd == "123":
            return f"{uname} 登录成功"
        else:
            return self.__jump(msg="用户名或密码错误")

6 装饰器

简言之,Python 装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数

使用 Python 装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。

  1. 在视图函数中使用自定义装饰器,那么自己定义的装饰器必须放在 app.route 下面。否则这个装饰器就起不到任何作用。
  2. 在类视图中使用装饰器,需要重写类视图的一个类属性 decorators,这个类属性是一个列表或者元组都可以,里面装的就是所有的装饰器。

6.1 案例 1

需求:查看设置个人信息时,只有检测到用户已经登录了才能查看,若没有登录,则无法查看并给出提示信息。

定义装饰器

def login_required(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        uname = request.values.get("uname")
        pwd = request.values.get("pwd")

        if uname == "xln" and pwd == "123":
            logging.info(f"{uname} 登录成功")
            return func(*args, **kwargs)
        else:
            logging.info(f"{uname} 尝试登录失败!!!")
            return "请先登录"

    return wrapper

使用装饰器

@app.route('/settings/')
@login_required
def settings():
    return '这是设置界面'

6.2 案例 2

需求:查看设置个人信息时,只有检测到用户已经登录了才能查看,若没有登录,则无法查看并给出提示信息。

使用装饰器

class ProfileView(View):
    decorators = [login_required]

    def dispatch_request(self):
        return '这是个人中心界面'


app.add_url_rule('/profile/', view_func=ProfileView.as_view('profile'))

7 蓝图介绍

在 Flask 中,使用蓝图 Blueprint 来分模块组织管理。

蓝图实际可以理解为是存储一组视图方法的容器对象,其具有如下特点:

  • 一个应用可以具有多个 Blueprint。
  • 可以将一个 Blueprint 注册到任何一个未使用的 URL 下比如 "/user/""/goods/"
  • Blueprint 可以单独具有自己的模板、静态文件或者其它的通用操作方法,它并不是必须要实现应用的视图和函数的。
  • 在一个应用初始化时,就应该要注册需要使用的 Blueprint。
注意
Blueprint 并不是一个完整的应用,它不能独立于应用运行,而必须要注册到某一个应用中。

7.1 使用方式

使用蓝图可以分为三个步骤:

  1. 创建一个蓝图对象。
user_bp = Blueprint('user', __name__)
  1. 在这个蓝图对象上,注册视图。
@user_bp.route('/')
def user_profile():
    return 'user_profile'
  1. 在应用对象上注册这个蓝图对象。
app.register_blueprint(user_bp)

7.2 单文件蓝图

可以将所有的创建蓝图对象与定义视图的操作放到一个文件中。这样做的话,单个文件过于庞大,代码结构混乱,不便于管理,也不利于分模块进行开发。

from flask import Flask, Blueprint

app = Flask(__name__)


@app.route("/")
def index():
    return "Hello"


# 创建蓝图
user_bp = Blueprint("user", __name__)


# 管理子路由
@user_bp.route("/login/")
def login():
    return "登录模块"


@user_bp.route("/register/")
def register():
    return "注册模块"


# 创建蓝图
item_bp = Blueprint("item", __name__)


# 管理子路由
@item_bp.route("/item/")
def item():
    return "产品模块"


# 注册蓝图
app.register_blueprint(user_bp, url_prefix="/user/")
app.register_blueprint(item_bp)


"""
用户模块
    登录模块
    注册模块
    退出模块
产品模块
    获取全部产品
    获取单件产品
    更新产品
    删除产品
    批量添加产品
收藏模块
购物车模块
"""

if __name__ == '__main__':
    app.run(debug=True)

7.3 指定蓝图的 url 前缀

在应用中注册蓝图时使用 url_prefix 参数指定。访问登录界面时,URL 就需要从 /login/ 替换为 /user/login/

app.register_blueprint(user_bp, url_prefix='/user/')
app.register_blueprint(goods_bp, url_prefix='/goods/')
思考
为什么需要为蓝图指定一个前缀?
---
为了方便管理,也为了防止冲突。
> > 如果不为蓝图指定前缀,那么在访问时不同蓝图中注册的视图 url 和在 app 下注册的视图 url 都是直接拼接在域名的后方,这样的话极易产生冲突。 > > 比如:用户蓝图 `user` 登录视图的 url 是 `/login/`,管理员蓝图 `admin` 登录视图的 url 也是 `login`,这样子的话,直接访问 `login` 可能就无法映射到我们想要的视图函数中。

8 蓝图的目录结构

为了让项目代码更加清晰,可以通过将代码分在不同的文件里进行管理。

8.1 根据功能模块

对于一个打算包含多个文件的蓝图,通常将创建蓝图对象放到 Python 包的 __init__.py 文件中。

--------- project  # 工程目录
  |------ main.py  # 项目启动文件
  |------ user  # 用户蓝图
  |  |--- __init__.py  # 此处创建蓝图对象
  |  |--- view.py  # 用户模块视图函数
  |  |--- ...
  |------ goods  # 商品蓝图
  |  |--- __init__.py
  |  |--- view.py
  |  |--- ...
  |...

第一版

目录结构:

main

"""项目启动文件"""
from flask import Flask
from user import user_bp
from item import item_bp

app = Flask(__name__)

# 注册蓝图
app.register_blueprint(user_bp, url_prefix="/user/")
app.register_blueprint(item_bp)

if __name__ == '__main__':
    app.run(debug=True)

user/view

"""用户蓝图"""
from flask import Blueprint

# 创建蓝图
user_bp = Blueprint("user", __name__)


# 管理子路由
@user_bp.route("/login/")
def login():
    return "登录模块"


@user_bp.route("/register/")
def register():
    return "注册模块"

item/view

"""商品蓝图"""
from flask import Blueprint

# 创建蓝图
item_bp = Blueprint("item", __name__)


# 管理子路由
@item_bp.route("/item/")
def item():
    return "产品模块"

第二版

将蓝图的定义放在功能模块下的 __init__.py 文件中,蓝图中视图函数的定义放在功能模块下的 view.py 文件中,并且在 __init__.py 文件的末尾导入 view.py 中的所有内容。这样在项目启动文件中导入蓝图对象时,就会自动加载蓝图的视图函数。

目录结构:

main

from flask import Flask
from user import user_bp
from item import item_bp

app = Flask(__name__)

# 注册蓝图
app.register_blueprint(user_bp, url_prefix="/user/")
app.register_blueprint(item_bp)

if __name__ == '__main__':
    app.run(debug=True)

user/__init__

from flask import Blueprint

# 创建蓝图
user_bp = Blueprint("user", __name__)

from user import view

user/view

from user import user_bp


# 管理子路由
@user_bp.route("/login/")
def login():
    return "登录模块"


@user_bp.route("/register/")
def register():
    return "注册模块"

item/__init__

from flask import Blueprint

# 创建蓝图
item_bp = Blueprint("item", __name__)

from item import view

item/view

from item import item_bp


# 管理子路由
@item_bp.route("/item/")
def item():
    return "产品模块"

__init__ 文件中,导入 view 的语句一定要放在蓝图创建的下方。

mian 中,为了方便功能模块的添加和管理应该将导入蓝图模块语句和蓝图注册语句写在一起。

from flask import Flask

app = Flask(__name__)

# 注册蓝图
from user import user_bp

app.register_blueprint(user_bp, url_prefix="/user/")

from item import item_bp

app.register_blueprint(item_bp)

if __name__ == '__main__':
    app.run(debug=True)

8.2 根据技术模块

--------- project  # 工程目录
  |------ main.py  # 项目启动文件
  |------ view  # 视图函数
  |  |--- __init__.py  # 此处创建蓝图对象
  |  |--- user.py  # 用户视图函数
  |  |--- item.py  # 产品视图
  |  |--- ...
  |...

目录结构:

main

from flask import Flask

app = Flask(__name__)

# 注册蓝图
from view import user_bp

app.register_blueprint(user_bp, url_prefix="/user/")

from view import item_bp

app.register_blueprint(item_bp)

if __name__ == '__main__':
    app.run(debug=True)

view/__init__

from flask import Blueprint

# 创建蓝图
user_bp = Blueprint("user", __name__)
# 导入用户视图
from view import user

# 创建蓝图
item_bp = Blueprint("item", __name__)
# 导入产品视图
from view import item

view/user

from view import user_bp


# 管理子路由
@user_bp.route("/login/")
def login():
    return "登录模块"


@user_bp.route("/register/")
def register():
    return "注册模块"

view/item

from view import item_bp


# 管理子路由
@item_bp.route("/item/")
def item():
    return "产品模块"

9 蓝图中模版文件

9.1 示例

main

from flask import Flask, render_template

app = Flask(__name__)


@app.route('/')
def index():
    return render_template("index.html")


from user import user_bp

app.register_blueprint(user_bp, url_prefix="/user")

if __name__ == '__main__':
    app.run(debug=True)

user/__init__

from flask import Blueprint

user_bp = Blueprint('user', __name__, template_folder='user_page')

from user import view

user/view

from flask import render_template

from user import user_bp


# 蓝图中,默认走最外层的 templates,如果,最外层找不到,才会找蓝图设置的独有文件目录。找不到设置的模板就会报错
# 蓝图中,如果想让独有的模板目录生效,需要在创建蓝图时,声明 template_folder 的路径地址

@user_bp.route("/")
def user_index():
    return render_template("user_index.html")

9.2 寻找规则

  1. 如果项目中的 templates 文件夹中有相应的模版文件,就直接使用了。
  2. 如果项目中的 templates 文件夹中没有相应的模版文件,那么就到在定义蓝图的时候指定的路径中寻找。
    • 并且蓝图中指定的路径可以为相对路径,相对的是当前这个蓝图文件所在的目录。

因为蓝图是在 user/__init__.py 中定义的,且在创建蓝图对象时指定 template_folder='user_page',所以如果没有在项目的 templates 中找到模板文件,就会到 user 这个文件夹下的 user_page 文件夹中寻找模版文件。

9.3 小总结

常规:蓝图文件在查找模版文件时,首先会以项目的 templates 为根目录进行查找。找不到时才会去蓝图指定的文件夹中寻找。

user_bp = Blueprint('user', __name__, url_prefix='/user', template_folder='user_page')
注意
1. 个性化 coder 喜欢在【创建蓝图对象的时候】指定**模版文件的查找路径**,如下:
> > ```python > news_bp = Blueprint('news', __name__, url_prefix='/news', template_folder='news_page') > ``` > > 1. 只有确定 templates 目录下没有对应的 html 文件名的时候,才会去蓝图文件指定的目录下查找,指定才会生效。 > > 2. 若 templates 目录下,有一个与蓝图文件指定的目录下同名的一个 html 文件时,优先走 templates 目录下的模板文件。

10 蓝图中静态文件

10.1 蓝图内部静态文件

蓝图对象创建时不会默认注册静态目录的路由。需要我们在创建时指定 static_folder 参数。

下面的示例将蓝图所在目录下的 user_static 目录设置为子应用的静态目录。

user_bp = Blueprint('user', __name__,
                    static_folder='user_static')
<h1>主应用</h1>
<video src="/static/aaa.mp4" controls muted autoplay loop></video>

<h1>子应用</h1>
<video src="/user/user_static/1.mp4" controls muted autoplay loop></video>

也可通过 static_url_path 改变访问路径。

user_bp = Blueprint('user', __name__,
                    static_folder='user_static',
                    static_url_path="ustatic")
<h1>子应用</h1>
<video src="/user/ustatic/1.mp4" controls muted autoplay loop></video>

11 蓝图 url_for 函数

先看一个例子:

main

from flask import Flask, url_for

app = Flask(__name__)


@app.route("/")
def index():
    return "Hello"


@app.route("/test/")
def test():
    return url_for("index")


from user import user_bp

app.register_blueprint(user_bp, url_prefix="/user")

if __name__ == '__main__':
    app.run(debug=True)

user.__init__

from flask import Blueprint

user_bp = Blueprint('user', __name__)

from user import view

user.view

from flask import url_for
from user import user_bp


@user_bp.route("/index/")
def index():
    return "用户主页"


@user_bp.route("/test/")
def test():
    return url_for("index")

上面例子中,/test/user/test 的结果分别是什么?


可以看到,两个页面中的 url_for 得到的地址都是 /,那我现在就想要得到子应用中 index 视图的 url,该怎么办呢?

url_for 函数中传递视图函数的名字时加上蓝图的名字。如 url_for("user.index")

@app.route("/test/")
def test():
    return url_for("user.index")


@user_bp.route("/test/")  
def test():  
    return url_for("user.index")



如果使用了蓝图,那么想要将蓝图中的视图函数反转为 url,就应该在使用 url_for 的时候指定这个蓝图名字。

app 类中、模版中、同一个蓝图类中都是如此。否则就找不到这个 endpoint。

html 文件中

<a href="{{ url_for('user.user_list')}}">新闻列表 OK写法</a>
    {# <a href="{{ url_for('user_list')}}">新闻列表 no Ok写法</a>#}

Python 文件中

from flask import Blueprint, render_template, url_for

user_bp = Blueprint('news',
                    __name__,
                    url_prefix='/user ',
                    template_folder='user_page',
                    static_folder='user_static')


@user_bp.route('/list/')
def user_list():
    # 如下写法:才找得到 url_for('蓝图名称.方法名')
    print(url_for('user.user_list'))  # /user/list /
    print(url_for('user.user_detail'))  # /user/detail/
    return render_template('user_list.html')


@user_bp.route('/detail/')
def user_detail():
    return '用户详情页面'

11.1 使用 url_for 引入静态文件

  1. 查找静态文件时,正常情况下,会在项目根目录下的 static 文件夹进行查找。

    <link href="{{ url_for('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">
    

12 子域名实现

蓝图实现子域名:我们模拟 ltg.com 这个域名的访问,由于我们的域名 ltg.com 是假的,为了能让这个域名正常访问到,我们在计算机的 host 文件中增加一个映射关系,将 127.0.0.1 映射到 ltg.com 上。

  1. 使用蓝图技术。

  2. 在创建蓝图对象的时候,需要传递一个 subdomain 参数,来指定这个子域名的前缀。

    user_bp = Blueprint('user', __name__, subdomain='user')
    
  3. 需要在主 app 文件中,需要配置 app.configSERVER_NAME 参数。例如:

    app.config['SERVER_NAME'] = 'ltg.com:5000'
    
  4. 在 windows:C:\Windows\System32\drivers\etc 下,找到 hosts 文件,然后添加域名与本机的映射。Linux:/etc/hosts
    域名和子域名都需要做映射:

    127.0.0.1 ltg.com
    127.0.0.1 user.ltg.com
    
注意
- ip 地址不能有子域名。
> - localhost 也不能有子域名。 > - 127.0.0.1 不能有子域名。

main

from flask import Flask

app = Flask(__name__)


@app.route("/")
def index():
    return "Hello"


from user import user_bp

app.register_blueprint(user_bp)

if __name__ == '__main__':
    app.config["SERVER_NAME"] = "ltg.com:5000"
    app.run(debug=True)

user.__init__

from flask import Blueprint

user_bp = Blueprint('user', __name__, subdomain='user')

from user import view

user.view

from user import user_bp


@user_bp.route('/login/')
def login():
    return '登录模板!'

posted @ 2026-04-11 02:04  挖掘鱼  阅读(2)  评论(0)    收藏  举报