【python】网页开发——Flask基础(二)路由和视图函数、HTTP请求方法、jinja2模板-CSDN博客

538 阅读12分钟

上一节回顾

【python】网页开发——Flask基础(一)介绍、安装、版本、入门程序、网页相关知识、项目配置

路由和视图函数

绑定方法一

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello World'

@app.route('/hello')
def print_hello():
 return 'Hello'


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

绑定方法二

from flask import Flask

app = Flask(__name__)


@app.route('/')
def hello_world():
    return 'Hello World'


def print_hello():
    return 'Hello'


app.add_url_rule('/hello', view_func=print_hello)

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

访问http://localhost:5000/hello,print_hello()函数的输出,将在浏览器中呈现。

Flask 变量规则

  1. <variable>: 使用尖括号包围的变量名称,表示该部分 URL 是一个变量。这个变量将作为参数传递给视图函数,默认情况下是字符串类型。
  2. <int:variable>: 表示该变量应该是一个整数。Flask 会自动将匹配到的字符串转换为整数,并传递给视图函数。
  3. <float:variable>: 表示该变量应该是一个浮点数。
  4. <path:variable>: 将匹配任何路径,包括斜杠。这个规则常用于捕获完整的 URL 路径。
  5. <string(length=2):variable>: 使用 string 转换器可以限制变量的类型为字符串,并且可以指定长度限制,例如 <string(length=2):variable> 表示该变量应该是一个长度为 2 的字符串。
  6. <any(值1, 值2, ...):variable>: 使用 any 转换器可以限制变量只能是指定列表中的某个值,例如 <any('admin', 'user'):variable> 表示该变量只能是 ‘admin’ 或 ‘user’。

简单示例:

from flask import Flask

app = Flask(__name__)

@app.route('/user/<username>')
def show_user_profile(username):
    return f"User: {username}"

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

URL 规则 /user/<username> 中的 <username> 表示一个变量,该变量将作为参数传递给 show_user_profile 视图函数。当用户访问 /user/john 时,视图函数将返回 “User: john”。

动态构建特定函数的URL

在访问某个URL时,如果访问用户是管理员,那么自动跳转到管理员页面;如果是访客,那么自动跳转到访客页面。

from flask import Flask, url_for, redirect

app = Flask(__name__)


@app.route('/admin')
def hello_admin():
    return 'Hello: admin'


@app.route('/guest/<guest>')
def hello_guest(guest):
    return f'Hello guest:{guest} '


@app.route('/user/<name>')
def hello_user(name):
    if name == 'admin':
        return redirect(url_for('hello_admin'))
    else:
        return redirect(url_for('hello_guest', guest=name))


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

HTTP请求方法

请求方法描述
GET获取资源
POST提交数据,创建新资源
PUT更新资源,通常是替换整个资源
PATCH更新资源的部分内容
DELETE删除指定资源
HEAD获取资源的头部信息,不包含实际内容
OPTIONS获取服务器支持的请求方法和功能
TRACE回显服务器接收到的请求,用于测试或诊断

GET请求

login.py

from flask import Flask, render_template, request, redirect, url_for

app = Flask(__name__)


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


@app.route('/welcome/<name>')
def welcome(name):
    return f'欢迎:{name}'


@app.route('/login', methods=['POST', 'GET'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        return redirect(url_for('welcome', name=username))
    else:
        username = request.args.get('username')
        return redirect(url_for('welcome', name=username))


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

login.html(必须放在templates文件夹下)

<!DOCTYPE html>
<html>
<body>

<h2>登录</h2>

<form action="/login" method="POST">
    <label for="username">用户名:</label>
    <input type="text" id="username" name="username"><br><br>

    <input type="submit" value="登录">
</form>

</body>
</html>
  • GET请求

浏览器输入http://127.0.0.1:5000/welcome/xiaoxiao跳转到http://127.0.0.1:5000/welcome/xiaoxiao页面

日志信息

127.0.0.1 - - [02/Oct/2023 23:31:04] "GET /login?username=xiaoxiao HTTP/1.1" 302 -
127.0.0.1 - - [02/Oct/2023 23:31:04] "GET /welcome/xiaoxiao HTTP/1.1" 200 -
  • POST请求

表单输入xiaoxiao点击登录,跳转到http://127.0.0.1:5000/welcome/xiaoxiao页面

日志信息

127.0.0.1 - - [02/Oct/2023 23:35:26] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [02/Oct/2023 23:35:33] "POST /login HTTP/1.1" 302 -
127.0.0.1 - - [02/Oct/2023 23:35:33] "GET /welcome/xiaoxiao HTTP/1.1" 200 -

通过HTML表单发送POST请求

login.py

from flask import Flask, render_template, request

app = Flask(__name__)

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

@app.route('/login', methods=['POST'])
def login():
    username = request.form['username']
    password = request.form['password']

    # 在这里执行登录逻辑
    # ...

    return f'登录成功!用户名:{username},密码:{password}'

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

login.html(必须放在templates文件夹下)

<!DOCTYPE html>
<html>
<body>

<h2>登录</h2>

<form action="/login" method="POST">
    <label for="username">用户名:</label>
    <input type="text" id="username" name="username"><br><br>

    <label for="password">密码:</label>
    <input type="password" id="password" name="password"><br><br>

    <input type="submit" value="登录">
</form>

</body>
</html>

jinja2模板

jinja2介绍

Jinja2 是一款现代化的,基于 Python 编写的模板引擎。它可以将数据和模板结合起来,生成最终的文本输出。Jinja2 具有高度的灵活性和可定制性,广泛应用于 Flask、Django 等 Web 框架中。

以下是 Jinja2 模板引擎的一些特点:

  1. 可以使用变量、表达式和流程控制语句。
  2. 支持模板继承和包含,可以提高代码重用性和可维护性。
  3. 灵活的模板配置和扩展系统,支持自定义过滤器和函数。
  4. 支持多种文本格式输出,例如 HTML、XML、JSON 等。
  5. 容易上手,学习成本低,且文档丰富。

jinja2官方文档:

jinja.palletsprojects.com/

Jinja2中文文档:

docs.jinkan.org/docs/jinja2…

Jinja2 入门示例

index.html(放在templates文件夹下)

<!DOCTYPE html>
<html>
  <head>
    <title>{{ title }}</title>
  </head>
  <body>
    <h1>{{ greeting }}</h1>
    <ul>
      {% for item in items %}
        <li>{{ item }}</li>
      {% endfor %}
    </ul>
  </body>
</html>

创建了一个包含变量和流程控制语句的 HTML 模板。{{ }} 符号用于输出变量,{% %} 符号用于包含流程控制语句。其中,titlegreetingitems 都是需要在渲染模板时传入的数据。

在 Flask 应用中使用 Jinja2 模板引擎非常简单。首先需要在 Flask 中配置模板文件所在的路径:

app = Flask(__name__)
app.template_folder = 'templates'
from flask import Flask, render_template

app = Flask(__name__)


@app.route('/')
def index():
    title = 'Jinja2 模板引擎介绍'
    greeting = '欢迎来到 Jinja2 的世界!'
    items = ['苹果', '香蕉', '桃子']
    return render_template('index.html', title=title, greeting=greeting, items=items)


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

变量名

中文文档:docs.jinkan.org/docs/jinja2…

在 Jinja2 模板中,变量名通常由字母、数字、下划线和点号组成。它们用于表示模板中的动态数据。

以下是 Jinja2 变量名的一些常见规则:

  1. 变量名只能包含字母、数字、下划线和点号。
  2. 变量名必须以字母或下划线开头。
  3. 变量名是区分大小写的,myVariablemyvariable 是两个不同的变量名。
  4. 变量名不能包含空格或特殊字符,如 !@# 等。
  5. 点号 (.) 用于表示对象的属性或字典的键。例如,user.name 表示取用户对象的 name 属性。

在 Jinja2 模板中,可以使用双花括号 ({{ }}) 来输出变量的值。例如:

<p>Hello, {{ name }}</p>

上述示例中,name 是一个变量,当渲染模板时,name 的值将会被替换到双花括号内部。

除了双花括号外,Jinja2 还提供了其他一些方式来使用变量,例如:

  • 控制语句: {% if variable %} ... {% endif %}
  • 循环语句: {% for item in collection %} ... {% endfor %}
  • 过滤器: {{ variable|filter }}

控制语句

在 Jinja2 模板中,控制语句用于实现条件判断、循环和逻辑操作等功能。常见的 Jinja2 控制语句包括 ifforelseelif 等。

下面是几种常见的 Jinja2 控制语句:

  1. if 语句:用于执行条件判断。
{% if condition %}
    ...  <!-- 如果条件为真,则执行这部分内容 -->
{% elif another_condition %}
    ...  <!-- 如果另一个条件为真,则执行这部分内容 -->
{% else %}
    ...  <!-- 如果以上条件都不满足,则执行这部分内容 -->
{% endif %}

示例:

{% if user %}
    <p>Welcome, {{ user }}!</p>
{% else %}
    <p>Please log in.</p>
{% endif %}
  1. for 语句:用于进行循环迭代。
{% for item in iterable %}
    ...  <!-- 在每次迭代中执行这部分内容 -->
{% endfor %}

示例:

<ul>
    {% for fruit in fruits %}
        <li>{{ fruit }}</li>
    {% endfor %}
</ul>
  1. else 语句:可与 forif 语句配合使用,在循环或条件判断不满足时执行。
{% for item in iterable %}
    ...
{% else %}
    ...  <!-- 在没有迭代项时执行这部分内容 -->
{% endfor %}

示例:

<ul>
    {% for fruit in fruits %}
        <li>{{ fruit }}</li>
    {% else %}
        <li>No fruits available.</li>
    {% endfor %}
</ul>

过滤器

Jinja2 过滤器是在模板中对变量进行处理和转换的工具。通过使用过滤器,可以对数据进行格式化、调整大小写、截断长度等操作,以满足特定需求。Jinja2 提供了一系列内置的过滤器,同时也支持自定义过滤器来扩展功能。

常用的 Jinja2 内置过滤器:

  1. 字符串处理过滤器:

    • capitalize:将字符串首字母大写,其余字母小写。
    • lower:将字符串转换为小写。
    • upper:将字符串转换为大写。
    • title:将字符串中每个单词的首字母大写。
  2. 数字处理过滤器:

    • abs:取绝对值。
    • round:四舍五入到指定位数。
    • float:将整数转换为浮点数。
  3. 列表和字典处理过滤器:

    • length:获取列表或字典的长度。
    • join:将列表中的项连接成一个字符串。
    • reverse:将列表反转。
  4. 日期和时间处理过滤器:

    • date:格式化日期对象。
    • time:格式化时间对象。
    • datetime:格式化日期时间对象。
  5. 其他常用过滤器:

    • default:如果变量为空或不存在,则使用默认值。
    • format:格式化字符串。
    • safe:标记字符串为安全的,不进行 HTML 转义。

使用过滤器时,可以通过管道符号 (|) 将变量和过滤器连接起来,多个过滤器可以按顺序使用。示例: {{ variable|filter1|filter2|... }}

除了内置过滤器,还可以自定义过滤器来实现特定的处理逻辑。自定义过滤器需要使用 Python 编写,并在应用中注册后才能使用。

测试器

Jinja2 测试器与过滤器类似,但它们返回的是布尔值而非字符串。测试器通常用于条件语句中,例如:

{% if my_variable is defined %}
    变量已定义
{% endif %}

在上面的示例中,is defined 就是一个测试器,用于检查变量 my_variable 是否已经定义。

  • abs: 如果变量的绝对值为真,返回 True。
  • any: 如果可迭代对象中至少有一个元素为真,返回 True。
  • all: 如果可迭代对象中所有元素都为真,返回 True。
  • callable: 如果变量是可调用的,返回 True。
  • defined: 如果变量已定义,返回 True。
  • divisibleby: 如果变量可被某个数整除,返回 True。
  • even: 如果变量为偶数,返回 True。
  • odd: 如果变量为奇数,返回 True。
  • equalto: 如果变量等于给定值,返回 True。
  • none: 如果变量为 None,返回 True。
  • lower: 如果字符串全为小写,返回 True。
  • upper: 如果字符串全为大写,返回 True。
  • string: 如果变量是字符串类型,返回 True。
  • number: 如果变量是数字类型,返回 True。
  • sequence: 如果变量是序列类型(列表、元组等),返回 True。
  • mapping: 如果变量是映射类型(字典等),返回 True。
  • iterable: 如果变量可迭代,返回 True。
  • bool: 如果变量为布尔类型,返回 True。
  • sameas: 如果变量与给定的对象相同(通过 is 运算符进行比较),返回 True。
  • differentfrom: 如果变量与给定的对象不相同,返回 True。
  • in: 如果变量在给定的可迭代对象中,返回 True。
  • notin: 如果变量不在给定的可迭代对象中,返回 True。

模板结构

宏和impor语句

在 Jinja2 模板中,可以使用宏(Macro)和 import 语句来组织和重用模板代码。

**宏(Macro)**是一段可重用的模板代码块,类似于函数或代码片段。通过定义宏,可以在模板中多次调用相同的代码块,提高代码的可重用性和可维护性。

如何定义和使用宏的简单示例:

{% macro hello(name) %}
    <h1>Hello, {{ name }}!</h1>
{% endmacro %}

{{ hello('John') }}
{{ hello('Alice') }}

在上面的示例中,定义了一个名为 hello 的宏,它接受一个参数 name 并显示一个包含问候消息的标题。然后两次调用了这个宏,并传递不同的名称作为参数,以生成不同的问候消息。

除了宏,还可以使用 import 语句来导入其他模板文件或模块中的代码。这样可以将模板的不同部分分别存储在不同的文件中,提高模板的组织性和可维护性。

下面是一个示例,展示了如何使用 import 语句导入其他模板文件中定义的宏:

<!-- macros.html -->
{% macro hello(name) %}
    <h1>Hello, {{ name }}!</h1>
{% endmacro %}

<!-- main.html -->
{% import 'macros.html' as macros %}

{{ macros.hello('John') }}
{{ macros.hello('Alice') }}

在上面的示例中,将宏定义存储在一个名为 macros.html 的文件中,并通过 import 语句将其导入到 main.html 中。然后使用 macros. 前缀调用导入的宏。

通过使用宏和 import 语句,可以更好地组织和管理模板代码,提高代码的可重用性和可维护性。

模板继承

模板继承是 Jinja2 中一项强大的功能,它允许创建一个基础模板,并在其他模板中继承和扩展这个基础模板,以实现代码的重用和模板的层次结构。

在模板继承中,可以定义一个称为基础模板或父模板(Base Template 或 Parent Template)的模板,其中包含了页面的整体结构和通用元素。然后,可以创建一个或多个子模板(Child Template),并继承基础模板,在子模板中只关注具体的内容。

base.html(基础模板)

<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}{% endblock %}</title>
</head>
<body>
    <header>
        {% block header %}{% endblock %}
    </header>

    <main>
        {% block content %}{% endblock %}
    </main>

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

创建了一个基础模板 base.html,其中定义了页面的整体结构,并使用 {% block ... %}{% endblock %} 标签来标记可被子模板填充的区域。

child.html(子模板)

{% extends 'base.html' %}

{% block title %}Welcome to My Website{% endblock %}

{% block header %}
    <h1>Welcome to My Website</h1>
    <nav>
        <ul>
            <li><a href="/">Home</a></li>
            <li><a href="/about">About</a></li>
            <li><a href="/contact">Contact</a></li>
        </ul>
    </nav>
{% endblock %}

{% block content %}
    <h2>About Us</h2>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
{% endblock %}

{% block footer %}
    <p>&copy; 2023 My Website. All rights reserved.</p>
{% endblock %}

创建了一个子模板 child.html,通过 {% extends 'base.html' %} 声明它继承自基础模板。在子模板中,可以使用 {% block ... %}{% endblock %} 标签来填充基础模板中的相应区域。这样,子模板只需要关注具体内容的填充,而不需要关注整体结构和通用元素。

当渲染 child.html 时,Jinja2 会自动将基础模板和子模板组合起来,生成最终的输出。

使用模板继承,可以轻松创建具有一致结构和外观的多个页面,并且当需要修改整体结构时,只需修改基础模板即可,所有继承自它的子模板都会自动更新。

闪现消息

flash() 是 Flask 中用于在重定向请求之间传递消息的函数。它可以在用户执行某个操作后,将消息存储在会话中,并在下一个请求中进行显示。

在你的代码示例中,flash() 函数用于在验证登录时显示成功或错误消息。在验证成功时,会将消息 '登录成功!' 存储为 'success' 类型的消息,以供在重定向后的页面上显示。在验证失败时,会将消息 '用户名或密码错误!' 存储为 'error' 类型的消息,同样在重定向后的页面上显示。

flash(message, category)

  • message 参数是要闪现的实际消息
  • category 参数是可选的,它可以是“error”,“info”或“warning”

flash.py

from flask import Flask, flash, redirect, render_template, url_for, request

app = Flask(__name__)
app.secret_key = 'your_secret_key'


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


@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        # 用户名和密码验证逻辑
        username = request.form['username']
        password = request.form['password']

        if username == 'admin' and password == 'password':
            flash('登录成功!', 'success')
            return redirect(url_for('login'))
        else:
            flash('用户名或密码错误!', 'error')
            return redirect(url_for('login'))

    return render_template('login.html')


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

login.html

<!DOCTYPE html>
<html>
<head>
    <title>Flask Flash Messages</title>
</head>
<body>
    {% with messages = get_flashed_messages() %}
        {% if messages %}
            <ul>
                {% for message in messages %}
                    <li>{{ message }}</li>
                {% endfor %}
            </ul>
        {% endif %}
    {% endwith %}

    <h1>登录页面</h1>
    <form action="/login" method="POST">
        <input type="text" name="username" placeholder="用户名" required><br>
        <input type="password" name="password" placeholder="密码" required><br>
        <input type="submit" value="登录">
    </form>
</body>
</html>

通过 get_flashed_messages() 函数获取闪现消息。然后,使用条件语句判断是否有消息需要显示,并使用循环来迭代所有的消息,并将它们显示为一个无序列表。