Flask基础教学(二)
1. 使用数据库
1.1 安装 Flask-SQLAlchemy
首先,使用 pip 安装 Flask-SQLAlchemy 扩展:
pip install Flask-SQLAlchemy
1.2 配置数据库
在 Flask 应用中配置数据库,例如我们将使用 SQLite 数据库:
from flask import Flask # 导入 Flask 类,用于创建 Flask 应用实例
from flask_sqlalchemy import SQLAlchemy # 导入 SQLAlchemy 类,用于管理数据库操作
app = Flask(__name__) # 创建一个 Flask 应用实例
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///example.db' # 配置数据库 URI,使用 SQLite 数据库并命名为 example.db
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # 禁用 SQLAlchemy 的事件系统,以减少内存开销
db = SQLAlchemy(app) # 创建一个 SQLAlchemy 实例,将当前 Flask 应用实例与数据库关联起来
1.3 定义数据模型
定义一个简单的用户模型:
# 定义 User 类,它继承自 db.Model
class User(db.Model):
# 设置 id 为整型,作为主键
id = db.Column(db.Integer, primary_key=True)
# 设置 username 为字符串类型,要求唯一,且不能为空
username = db.Column(db.String(80), unique=True, nullable=False)
# 设置 email 为字符串类型,要求唯一,且不能为空
email = db.Column(db.String(120), unique=True, nullable=False)
# 定义 __repr__ 方法,返回表示该对象的字符串形式
def __repr__(self):
return f'<User {self.username}>'
这段代码定义了一个名为 User
的类,它继承自 db.Model
。这个类用于表示用户数据,并包含 id
、username
和 email
三个属性。__repr__
方法用于定义对象的字符串表示形式。
创建数据库表:
db.create_all()
1.4 增删改查
插入一条新记录:
# 创建一个新的 User 对象,设置用户名为 'John',电子邮件为 'john@example.com'
new_user = User(username='John', email='john@example.com')
# 将新创建的 User 对象添加到数据库会话中,以便将其保存到数据库中
db.session.add(new_user)
# 提交数据库会话中的更改,将新创建的 User 对象保存到数据库中
db.session.commit()
查询所有用户:
users = User.query.all() # 查询数据库中的所有用户,并将结果赋值给变量 "users"
print(users) # 打印 "users" 变量的内容,即所有用户的信息
查询单个用户:
# 查询数据库中用户名为 'John' 的用户
user = User.query.filter_by(username='John').first()
# 打印查询结果(如果找到用户,将显示用户信息;如果没有找到,将显示 None)
print(user)
这段代码首先使用 User.query.filter_by(username='John')
查询数据库中用户名为 ‘John’ 的用户。然后,使用 first()
方法获取查询结果中的第一个用户。如果没有找到匹配的用户,first()
将返回 None
。最后,我们打印查询结果。如果找到用户,将显示用户信息;如果没有找到,将显示 None
。
更新用户:
user.username = 'Jane' # 修改 user 对象的 username 属性值为 'Jane'
db.session.commit() # 提交更改,将更新后的 user 对象保存到数据库
删除用户:
db.session.delete(user) # 删除 User 对象实例,表示从数据库中删除这个用户
db.session.commit() # 提交数据库会话的更改,将删除操作保存到数据库中
1.5 完整示例
# 导入所需模块
from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
# 创建 Flask 应用实例
app = Flask(__name__)
# 配置数据库连接
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///example.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# 初始化 SQLAlchemy 对象
db = SQLAlchemy(app)
# 创建 User 模型
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
def __repr__(self):
return f'<User {self.username}>'
# 创建数据库表
db.create_all()
# 定义添加用户的路由
@app.route('/add_user', methods=['POST'])
def add_user():
# 从表单数据中获取用户名和电子邮件
username = request.form['username']
email = request.form['email']
# 创建新用户对象
new_user = User(username=username, email=email)
# 将新用户添加到数据库会话并提交
db.session.add(new_user)
db.session.commit()
# 重定向回首页
return redirect(url_for('index'))
# 定义显示所有用户的路由
@app.route('/users')
def users():
# 查询所有用户
users = User.query.all()
# 渲染用户列表模板
return render_template('users.html', users=users)
# 定义首页路由
@app.route('/')
def index():
# 渲染首页模板
return render_template('index.html')
# 运行 Flask 应用
if __name__ == '__main__':
app.run()
在这个示例中,我们创建了一个 User 模型来表示用户数据。我们使用 db.create_all()
创建数据库表。然后,我们定义了两个路由,/add_user
用于添加新用户,/users
用于显示所有用户。
以下是一个简单的 users.html
模板文件,用于显示用户列表:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Users List</title>
</head>
<body>
<h1>Users List</h1>
<table>
<thead>
<tr>
<th>ID</th>
<th>Username</th>
<th>Email</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{ user.id }}</td>
<td>{{ user.username }}</td>
<td>{{ user.email }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<a href="{{ url_for('index') }}">Back to Home</a>
</body>
</html>
这个模板文件包含一个表格,用于显示用户的 ID、用户名和电子邮件。{% for user in users %}
和 {% endfor %}
之间的代码会为 users
列表中的每个用户生成一个表格行。{{ user.id }}
、{{ user.username }}
和 {{ user.email }}
分别用于显示用户的 ID、用户名和电子邮件。在页面底部,还有一个链接可以返回到主页。
以下是一个简单的 index.html
文件内容。该文件包含一个表单,用于输入用户名和电子邮件,以及一个链接,用于浏览所有用户:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Flask App</title>
</head>
<body>
<h1>Welcome to the Flask App</h1>
<form method="POST" action="{{ url_for('add_user') }}">
<label for="username">Username:</label>
<input type="text" id="username" name="username" required>
<br>
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
<br>
<input type="submit" value="Add User">
</form>
<br>
<a href="{{ url_for('users') }}">View All Users</a>
</body>
</html>
这个 index.html
模板包含了一个表单,用于提交用户名和电子邮件到 /add_user
路由。表单的 action
属性设置为 {{ url_for('add_user') }}
,这样 Flask 就知道将表单数据提交到哪个路由。页面底部的链接指向用户列表页面,通过 {{ url_for('users') }}
指定。
2. 用户认证
2.1 安装 Flask-Login
使用 pip 安装 Flask-Login 扩展:
pip install Flask-Login
2.2 配置用户认证
在 Flask 应用中配置 Flask-Login:
from flask_login import LoginManager # 导入 Flask-Login 的 LoginManager 类
login_manager = LoginManager() # 创建一个 LoginManager 对象
login_manager.init_app(app) # 将 Flask 应用初始化到 LoginManager 对象中
from flask_login import LoginManager
:这行代码从flask_login
模块中导入LoginManager
类。LoginManager
类用于处理用户登录、登出和会话管理。login_manager = LoginManager()
:这行代码创建了一个LoginManager
类的实例,用于处理用户认证相关的操作。login_manager.init_app(app)
:这行代码将 Flask 应用app
初始化到login_manager
实例中。这使得login_manager
可以与 Flask 应用一起工作,处理用户认证相关的功能。
2.3 创建用户模型
更新用户模型以支持 Flask-Login:
from flask_login import UserMixin
# 定义 User 类,继承自 UserMixin 和 db.Model
class User(UserMixin, db.Model):
# ...
为 Flask-Login 定义一个加载用户的函数:
# 注册 Flask-Login 的用户加载回调函数
@login_manager.user_loader
def load_user(user_id):
# 将 user_id 转换为整数,因为从会话中获取的 user_id 是字符串类型
user_id = int(user_id)
# 使用 user_id 从数据库中查询用户,并返回该用户对象
return User.query.get(user_id)
在这段代码中,我们为 Flask-Login 定义了一个用户加载回调函数。这个函数接受一个参数 user_id
,表示要加载的用户的唯一标识。我们需要将这个标识转换为整数(因为从会话中获取的 user_id 是字符串类型),然后使用 User.query.get(user_id)
从数据库中查询该用户。最后,我们返回查询到的用户对象。Flask-Login 会使用这个函数在需要时自动加载用户。
2.4 用户注册与登录
创建注册和登录路由:
from flask import render_template, request, redirect, url_for # 导入所需的库
from flask_login import login_user, logout_user # 导入登录、登出相关函数
# 定义注册路由
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'POST': # 当提交注册表单时
username = request.form['username'] # 获取表单中的用户名
email = request.form['email'] # 获取表单中的电子邮件
new_user = User(username=username, email=email) # 创建新用户对象
db.session.add(new_user) # 添加新用户对象到数据库会话
db.session.commit() # 提交数据库会话以保存更改
return redirect(url_for('login')) # 重定向到登录页面
return render_template('register.html') # 渲染注册页面模板
# 定义登录路由
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST': # 当提交登录表单时
username = request.form['username'] # 获取表单中的用户名
user = User.query.filter_by(username=username).first() # 查询与用户名匹配的用户
if user: # 如果找到用户
login_user(user) # 使用 Flask-Login 进行登录操作
return redirect(url_for('index')) # 重定向到主页
return render_template('login.html') # 渲染登录页面模板
创建登出路由:
from flask_login import login_required # 导入 login_required 装饰器,用于保护需要登录才能访问的视图函数
@app.route('/logout') # 为登出功能定义路由
@login_required # 使用 login_required 装饰器,确保只有已登录用户才能访问此视图函数
def logout(): # 定义登出视图函数
logout_user() # 使用 Flask-Login 的 logout_user 函数,登出当前用户
return redirect(url_for('index')) # 登出成功后,重定向到主页
这段代码主要是实现了用户登出功能。通过使用 Flask-Login 的 login_required
装饰器,我们确保了只有已登录的用户才能访问此视图函数。在视图函数内部,我们调用了 logout_user()
函数来登出当前用户,然后将用户重定向到主页。
2.5 保护路由
使用 login_required
装饰器保护需要用户登录的路由:
# 使用 Flask 的路由装饰器定义一个名为 dashboard 的路由,响应 '/dashboard' URL
@app.route('/dashboard')
# 使用 Flask-Login 的 login_required 装饰器,确保只有已登录用户才能访问此路由
@login_required
# 定义名为 dashboard 的视图函数
def dashboard():
# 使用 Flask 的 render_template 函数渲染名为 'dashboard.html' 的模板文件,并返回结果
return render_template('dashboard.html')
此代码定义了一个名为 dashboard
的路由,它响应 /dashboard
URL。使用 Flask-Login 的 login_required
装饰器确保只有已登录的用户才能访问此路由。当用户访问此路由时,视图函数 dashboard
会渲染名为 dashboard.html
的模板文件并返回结果。
2.6 完整示例
from flask import Flask, render_template, request, redirect, url_for, flash
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
app = Flask(__name__) # 创建 Flask 应用实例
app.config['SECRET_KEY'] = 'mysecretkey' # 设置应用的密钥
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///auth_example.db' # 设置数据库的 URI
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # 关闭 SQLAlchemy 的修改跟踪功能
db = SQLAlchemy(app) # 创建 SQLAlchemy 实例
login_manager = LoginManager() # 创建 LoginManager 实例
login_manager.init_app(app) # 初始化 LoginManager 实例
login_manager.login_view = 'login' # 设置未登录用户的跳转地址
class User(UserMixin, db.Model): # 创建 User 模型
id = db.Column(db.Integer, primary_key=True) # id 主键
username = db.Column(db.String(80), unique=True, nullable=False) # 用户名,必须唯一且不能为空
email = db.Column(db.String(120), unique=True, nullable=False) # 邮箱,必须唯一且不能为空
password = db.Column(db.String(255), nullable=False) # 密码,不能为空
def __repr__(self):
return f'<User {self.username}>' # 返回用户的用户名
db.create_all() # 创建数据库表
@login_manager.user_loader
def load_user(user_id): # 定义用户加载函数
return User.query.get(int(user_id)) # 通过 id 获取用户对象
@app.route('/register', methods=['GET', 'POST']) # 定义注册路由
def register():
if current_user.is_authenticated: # 如果已登录,则直接跳转到主页
return redirect(url_for('index'))
if request.method == 'POST': # 如果是 POST 请求
username = request.form['username']
email = request.form['email']
password = generate_password_hash(request.form['password'], method='sha256') # 对密码进行哈希加密
new_user = User(username=username, email=email, password=password) # 创建新用户
db.session.add(new_user) # 添加新用户到数据库
db.session.commit() # 提交更改
flash('Registration successful. You can now log in.', 'success') # 提示用户注册成功,可以登录
return redirect(url_for('login'))
return render_template('register.html') # 返回注册页面
@app.route('/login', methods=['GET', 'POST']) # 定义登录路由
def login():
if current_user.is_authenticated: # 如果已登录,则直接跳转到主页
return redirect(url_for('index'))
if request.method == 'POST': # 如果是 POST 请求
username = request.form['username']
password = request.form['password']
remember_me = 'remember_me' in request.form
user = User.query.filter_by(username=username).first() # 查询用户
if user and check_password_hash(user.password, password): # 如果用户存在且密码正确
login_user(user, remember=remember_me) # 登录用户
flash('Logged in successfully.', 'success')
return redirect(url_for('index')) # 跳转到主页
else:
flash('Invalid username or password.', 'danger') # 提示用户名或密码错误
return render_template('login.html') # 返回登录页面
@app.route('/logout') # 定义登出路由
@login_required # 设置路由需要登录才能访问
def logout():
logout_user() # 登出用户
flash('Logged out successfully.', 'success') # 提示用户登出成功
return redirect(url_for('login')) # 跳转到登录页面
@app.route('/') # 定义主页路由
@login_required # 设置路由需要登录才能访问
def index():
return render_template('index.html') # 返回主页模板
if __name__ == '__main__':
app.run() # 运行 Flask 应用
在这个示例中,我们创建了一个 User 模型来表示用户数据。我们使用 db.create_all()
创建数据库表。我们还配置了 Flask-Login 来处理用户认证。我们定义了四个路由:/register
用于用户注册,/login
用于用户登录,/logout
用于用户登出,以及 /
作为主页,需要用户登录才能访问。
以下是一个简单的 login.html
文件内容示例,用于展示登录表单和错误提示信息:
{% extends 'base.html' %}
{% block content %}
<h1>Login</h1>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}" role="alert">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endwith %}
<form method="POST" action="{{ url_for('login') }}">
{{ form.csrf_token }}
<div class="form-group">
<label for="username">Username</label>
{{ form.username(class="form-control", placeholder="Enter username", autofocus=true) }}
</div>
<div class="form-group">
<label for="password">Password</label>
{{ form.password(class="form-control", placeholder="Enter password") }}
</div>
<div class="form-group form-check">
{{ form.remember_me(class="form-check-input") }}
<label class="form-check-label" for="remember_me">Remember me</label>
</div>
<button type="submit" class="btn btn-primary">Login</button>
</form>
{% endblock %}
其中,使用了 Flask 提供的模板语言 Jinja2
的语法,用于渲染动态内容。在表单提交时,action
属性会将请求发送到 Flask 应用中的 /login
路由。在表单中,使用了 WTForms 生成的 form.username
、form.password
和 form.remember_me
等字段。get_flashed_messages()
函数用于获取 Flask 中的闪现消息,以便在模板中显示成功或失败的提示信息。
以下是一个简单的 register.html 文件的示例,用于展示注册表单和错误提示信息:
{% extends 'base.html' %}
{% block content %}
<div class="row justify-content-md-center mt-5">
<div class="col-md-6">
<div class="card">
<div class="card-body">
<h3 class="card-title text-center">Create an Account</h3>
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message[1] }}" role="alert">
{{ message[0] }}
</div>
{% endfor %}
{% endif %}
{% endwith %}
<form method="post">
<div class="form-group">
<label for="username">Username</label>
<input type="text" class="form-control" id="username" name="username" placeholder="Enter username" required>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" class="form-control" id="email" name="email" placeholder="Enter email" required>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control" id="password" name="password" placeholder="Password" required>
</div>
<button type="submit" class="btn btn-primary btn-block">Create Account</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
该模板继承了 base.html 模板,包含一个注册表单和用于显示错误消息的逻辑。在提交表单时,将会向 /register 路由发起 POST 请求。
以下是一个简单的 index.html
文件示例,包含欢迎信息和注销按钮:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Welcome</title>
</head>
<body>
<h1>Welcome, {{ current_user.username }}!</h1>
<form action="{{ url_for('logout') }}" method="post">
<input type="submit" value="Logout">
</form>
</body>
</html>
其中 {{ current_user.username }}
显示当前已登录用户的用户名, url_for('logout')
返回注销路由的 URL,注销按钮通过 POST
方法提交表单,触发注销操作。