在上一篇教程中,我们掌握了 Flask 的基础知识,包括安装、第一个应用、路由和视图函数。本篇将带领大家进入 Flask 开发的更核心领域:模板渲染、表单处理 和 数据库操作。通过这些内容,你将能够构建出功能完整、交互友好且具备数据持久化能力的 Web 应用。
在实际项目中,我们通常不会在视图函数中直接返回 HTML 字符串,而是将 HTML 代码放在独立的模板文件中,并通过模板引擎动态填充数据。Flask 默认使用 Jinja2 模板引擎。
首先,在项目根目录下创建 templates 文件夹,用于存放所有模板文件。创建一个简单的模板 index.html:
<!DOCTYPEhtml>
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<h1>Hello, {{ name }}!</h1>
</body>
</html>
在视图函数中,使用 render_template 函数渲染模板并传递参数:
from flask import render_template
@app.route('/')
defindex():
return render_template('index.html', title='Home', name='Alice')
Jinja2 支持多种语法:
{{ variable }}{% if user %}
<p>Hello, {{ user }}</p>
{% else %}
<p>Hello, Guest</p>
{% endif %}
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
| 对变量进行格式化,如 {{ name|upper }}、{{ date|datetime }}(需自定义)。{% macro input(name, value='', type='text') %}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}
模板继承是 Jinja2 最强大的功能之一,可以避免重复代码。定义一个基础模板 base.html:
<!DOCTYPEhtml>
<html>
<head>
<title>{% block title %}{% endblock %} - My App</title>
</head>
<body>
<header>
<h1>My App</h1>
</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>
<p>© 2023</p>
</footer>
</body>
</html>
子模板通过 extends 继承,并填充 block:
{% extends "base.html" %}
{% block title %}Home{% endblock %}
{% block content %}
<p>Welcome to the homepage!</p>
{% endblock %}
静态文件(CSS、JS、图片)应放在 static 文件夹中。在模板中,使用 url_for('static', filename='style.css') 生成正确路径:
<linkrel="stylesheet"href="{{ url_for('static', filename='style.css') }}">
表单是用户与 Web 应用交互的主要方式。Flask 本身提供了基础的请求数据处理,但推荐使用 Flask-WTF 扩展来简化表单创建、验证和 CSRF 保护。
pip install flask-wtf
表单类继承自 FlaskForm,字段类型由 WTForms 提供。创建一个简单的登录表单:
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Email, Length
classLoginForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired(), Length(min=6)])
submit = SubmitField('Login')
在视图函数中创建表单实例,并检查 validate_on_submit() 来处理提交。
from flask import render_template, redirect, url_for
@app.route('/login', methods=['GET','POST'])
deflogin():
form = LoginForm()
if form.validate_on_submit():
# 表单验证通过,处理登录逻辑
email = form.email.data
password = form.password.data
# 实际开发中应验证数据库中的用户
return redirect(url_for('index'))
return render_template('login.html', form=form)
Jinja2 可以方便地渲染表单字段和错误信息:
{% extends "base.html" %}
{% block content %}
<formmethod="POST"action="">
{{ form.hidden_tag() }} <!-- 生成 CSRF token -->
<div>
{{ form.email.label }}<br>
{{ form.email(size=32) }}<br>
{% for error in form.email.errors %}
<spanstyle="color: red;">[{{ error }}]</span>
{% endfor %}
</div>
<div>
{{ form.password.label }}<br>
{{ form.password(size=32) }}<br>
{% for error in form.password.errors %}
<spanstyle="color: red;">[{{ error }}]</span>
{% endfor %}
</div>
<div>{{ form.submit() }}</div>
</form>
{% endblock %}
Flask-WTF 默认启用 CSRF 保护。你需要在应用配置中设置一个密钥:
app.config['SECRET_KEY']='your-secret-key-here'
表单中的 {{ form.hidden_tag() }} 会生成一个隐藏字段,包含 CSRF 令牌。
Flask 本身不绑定数据库,但推荐使用 Flask-SQLAlchemy 作为 ORM 工具,它简化了数据库操作。我们还会使用 Flask-Migrate 来管理数据库迁移。
pip install flask-sqlalchemy flask-migrate
在应用配置中设置数据库 URI(以 SQLite 为例):
app.config['SQLALCHEMY_DATABASE_URI']='sqlite:///app.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS']=False
初始化 SQLAlchemy 和 Migrate:
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
db = SQLAlchemy(app)
migrate = Migrate(app, db)
模型类继承自 db.Model,每个类对应一张数据库表。例如,创建一个用户模型:
classUser(db.Model):
id= db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password_hash = db.Column(db.String(128))
def__repr__(self):
returnf'<User {self.username}>'
使用 Flask-Migrate 可以自动化地生成数据库迁移脚本,避免手动操作。
初始化迁移仓库(只需一次):
flask db init
生成迁移脚本(当模型改变时):
flask db migrate -m"create user table"
应用迁移到数据库:
flask db upgrade
在视图函数中操作数据库。示例:注册用户
from werkzeug.security import generate_password_hash
@app.route('/register', methods=['GET','POST'])
defregister():
form = RegistrationForm()
if form.validate_on_submit():
# 检查用户名或邮箱是否已存在
user = User.query.filter_by(username=form.username.data).first()
if user:
flash('Username already exists','danger')
return redirect(url_for('register'))
# 创建新用户
new_user = User(
username=form.username.data,
email=form.email.data,
password_hash=generate_password_hash(form.password.data)
)
db.session.add(new_user)
db.session.commit()
flash('Registration successful!','success')
return redirect(url_for('login'))
return render_template('register.html', form=form)
常用查询方法:
User.query.all()User.query.filter_by(username='alice').first()User.query.get(1)User.query.filter(User.email.endswith('@example.com')).all()User.query.paginate(page=1, per_page=20)定义一对多关系。例如,一个用户可以有多个文章:
classUser(db.Model):
# ...
posts = db.relationship('Post', backref='author', lazy='dynamic')
classPost(db.Model):
id= db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100))
body = db.Column(db.Text)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
db.relationshipbackref='author'post.author 获取作者。lazy='dynamic'user.posts 返回一个查询对象,而不是立即加载所有文章。多对多关系需要借助关联表。例如,用户和角色:
# 关联表
roles_users = db.Table('roles_users',
db.Column('user_id', db.Integer, db.ForeignKey('user.id')),
db.Column('role_id', db.Integer, db.ForeignKey('role.id'))
)
classRole(db.Model):
id= db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), unique=True)
users = db.relationship('User', secondary=roles_users, backref='roles')
当应用规模变大时,将所有路由放在一个文件中会变得混乱。Flask 提供了蓝图来模块化路由和视图。
创建一个 auth 蓝图,处理认证相关路由:
# auth.py
from flask import Blueprint, render_template
auth_bp = Blueprint('auth', __name__, url_prefix='/auth')
@auth_bp.route('/login')
deflogin():
return render_template('auth/login.html')
在应用工厂中注册蓝图:
defcreate_app():
app = Flask(__name__)
# ...
from.auth import auth_bp
app.register_blueprint(auth_bp)
return app
在模板或视图中,使用带蓝图名的 url_for:
url_for('auth.login')# 生成 /auth/login
Flask 允许自定义错误页面,提升用户体验。
@app.errorhandler(404)
defpage_not_found(e):
return render_template('404.html'),404
@app.errorhandler(500)
definternal_server_error(e):
return render_template('500.html'),500
蓝图也可以有自己的错误处理器,但作用范围仅限于该蓝图下的路由。
在本篇教程中,我们学习了:
掌握了这些内容后,你已经具备了构建完整 Web 应用的能力。在第三篇(也是最后一篇)教程中,我们将深入探讨 Flask 的高级主题,包括:中间件与扩展、项目部署,并给出一个综合示例,将所学知识串联起来。
请继续关注第三篇,我们将在那里完成 Flask 学习的最后一块拼图。如果你对第二篇有任何疑问,欢迎随时提出。
欢迎关注公众号,感谢对文章的点赞分享喜欢,冉成未来会持续更新前后端开发技术、人工智能技术、IT相关的文章及学习经验、知识分享,未来虽然充满着不确定性,但我们可以不断提升自己,不断为未来做准备,让未来更好的自己成就更美好的未来。
Python基础系列 | Python之PyQt5基础知识(五)
Python基础系列 | Python之PyQt5基础知识(四)
Python基础系列 | Python之PyQt5基础知识(三)
Python基础系列 | Python之PyQt5基础知识(二)
Python基础系列 | Python之PyQt5基础知识(一)