上一节回顾
【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 变量规则
<variable>: 使用尖括号包围的变量名称,表示该部分 URL 是一个变量。这个变量将作为参数传递给视图函数,默认情况下是字符串类型。<int:variable>: 表示该变量应该是一个整数。Flask 会自动将匹配到的字符串转换为整数,并传递给视图函数。<float:variable>: 表示该变量应该是一个浮点数。<path:variable>: 将匹配任何路径,包括斜杠。这个规则常用于捕获完整的 URL 路径。<string(length=2):variable>: 使用string转换器可以限制变量的类型为字符串,并且可以指定长度限制,例如<string(length=2):variable>表示该变量应该是一个长度为 2 的字符串。<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 模板引擎的一些特点:
- 可以使用变量、表达式和流程控制语句。
- 支持模板继承和包含,可以提高代码重用性和可维护性。
- 灵活的模板配置和扩展系统,支持自定义过滤器和函数。
- 支持多种文本格式输出,例如 HTML、XML、JSON 等。
- 容易上手,学习成本低,且文档丰富。
jinja2官方文档:
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 模板。{{ }} 符号用于输出变量,{% %} 符号用于包含流程控制语句。其中,title、greeting 和 items 都是需要在渲染模板时传入的数据。
在 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 变量名的一些常见规则:
- 变量名只能包含字母、数字、下划线和点号。
- 变量名必须以字母或下划线开头。
- 变量名是区分大小写的,
myVariable和myvariable是两个不同的变量名。 - 变量名不能包含空格或特殊字符,如
!、@、#等。 - 点号 (
.) 用于表示对象的属性或字典的键。例如,user.name表示取用户对象的 name 属性。
在 Jinja2 模板中,可以使用双花括号 ({{ }}) 来输出变量的值。例如:
<p>Hello, {{ name }}</p>
上述示例中,name 是一个变量,当渲染模板时,name 的值将会被替换到双花括号内部。
除了双花括号外,Jinja2 还提供了其他一些方式来使用变量,例如:
- 控制语句:
{% if variable %} ... {% endif %} - 循环语句:
{% for item in collection %} ... {% endfor %} - 过滤器:
{{ variable|filter }}
控制语句
在 Jinja2 模板中,控制语句用于实现条件判断、循环和逻辑操作等功能。常见的 Jinja2 控制语句包括 if、for、else、elif 等。
下面是几种常见的 Jinja2 控制语句:
if语句:用于执行条件判断。
{% if condition %}
... <!-- 如果条件为真,则执行这部分内容 -->
{% elif another_condition %}
... <!-- 如果另一个条件为真,则执行这部分内容 -->
{% else %}
... <!-- 如果以上条件都不满足,则执行这部分内容 -->
{% endif %}
示例:
{% if user %}
<p>Welcome, {{ user }}!</p>
{% else %}
<p>Please log in.</p>
{% endif %}
for语句:用于进行循环迭代。
{% for item in iterable %}
... <!-- 在每次迭代中执行这部分内容 -->
{% endfor %}
示例:
<ul>
{% for fruit in fruits %}
<li>{{ fruit }}</li>
{% endfor %}
</ul>
else语句:可与for或if语句配合使用,在循环或条件判断不满足时执行。
{% 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 内置过滤器:
-
字符串处理过滤器:
capitalize:将字符串首字母大写,其余字母小写。lower:将字符串转换为小写。upper:将字符串转换为大写。title:将字符串中每个单词的首字母大写。
-
数字处理过滤器:
abs:取绝对值。round:四舍五入到指定位数。float:将整数转换为浮点数。
-
列表和字典处理过滤器:
length:获取列表或字典的长度。join:将列表中的项连接成一个字符串。reverse:将列表反转。
-
日期和时间处理过滤器:
date:格式化日期对象。time:格式化时间对象。datetime:格式化日期时间对象。
-
其他常用过滤器:
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>© 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() 函数获取闪现消息。然后,使用条件语句判断是否有消息需要显示,并使用循环来迭代所有的消息,并将它们显示为一个无序列表。