Flask入门教程(十三):数据库操作——SQLAlchemy与CRUD完整实践

1. 使用Flask-SQLAlchemy

SQLAlchemy是一个强大的ORM(对象关系映射)库,通过Python对象与数据库表进行交互,无需编写SQL语句。Flask-SQLAlchemy是Flask的官方扩展,集成了SQLAlchemy。

安装Flask-SQLAlchemy

复制代码
pip install flask-sqlalchemy

配置SQLAlchemy

复制代码
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
# SQLite数据库(开发环境推荐)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///example.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

2. 定义模型

模型是数据库表的Python类,每个模型类代表数据库中的一张表。

复制代码
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)
    age = db.Column(db.Integer, nullable=True)

    def __repr__(self):
        return f'<User {self.username}>'

常用字段类型

字段类型 说明 Python类型
db.Integer 整数 int
db.String(n) 可变长度字符串 str
db.Text 长文本 str
db.Boolean 布尔值 bool
db.DateTime 日期时间 datetime.datetime
db.Date 日期 datetime.date
db.Float 浮点数 float
db.Decimal 高精度小数 decimal.Decimal
db.JSON JSON数据 dict / list

常用字段参数

参数 说明
primary_key=True 主键
unique=True 值唯一
nullable=False 不允许为空
default=value 默认值
index=True 创建索引
autoincrement=True 自增(整数主键默认)

3. 创建和管理数据库

在定义了模型后,需要在应用上下文中创建数据库和表:

复制代码
with app.app_context():
    db.create_all()  # 创建所有模型对应的表

删除所有表(谨慎使用)

复制代码
with app.app_context():
    db.drop_all()  # 删除所有表,数据会丢失

4. CRUD操作

创建记录(Create)

复制代码
@app.route('/add_user')
def add_user():
    new_user = User(username='john_doe', email='john@example.com', age=25)
    db.session.add(new_user)   # 添加到会话
    db.session.commit()        # 提交事务,保存到数据库
    return 'User added!'

# 批量添加
@app.route('/add_users')
def add_users():
    users = [
        User(username='alice', email='alice@example.com'),
        User(username='bob', email='bob@example.com'),
    ]
    db.session.add_all(users)
    db.session.commit()
    return 'Users added!'

读取记录(Read)

复制代码
# 查询所有记录
@app.route('/get_users')
def get_users():
    users = User.query.all()
    return '<br>'.join([f'{user.username} ({user.email})' for user in users])

# 通过主键查询单个记录
user = User.query.get(1)  # 查询id=1的用户

# 通过条件查询(精确匹配)
user = User.query.filter_by(username='john_doe').first()

# 通过条件查询(多个条件)
users = User.query.filter_by(username='john_doe', age=25).all()

# 获取第一条记录
user = User.query.first()

更新记录(Update)

复制代码
@app.route('/update_user/<int:user_id>')
def update_user(user_id):
    user = User.query.get(user_id)
    if user:
        user.username = 'new_username'
        user.age = 26
        db.session.commit()
        return 'User updated!'
    return 'User not found!'

删除记录(Delete)

复制代码
@app.route('/delete_user/<int:user_id>')
def delete_user(user_id):
    user = User.query.get(user_id)
    if user:
        db.session.delete(user)
        db.session.commit()
        return 'User deleted!'
    return 'User not found!'

5. 高级查询操作

条件查询

复制代码
from sqlalchemy import or_, and_, not_

# AND条件(默认)
users = User.query.filter(User.username == 'john_doe', User.age > 18).all()

# OR条件
users = User.query.filter(
    or_(User.username == 'john_doe', User.email == 'john@example.com')
).all()

# AND显式
users = User.query.filter(
    and_(User.age >= 18, User.age <= 60)
).all()

# NOT条件
users = User.query.filter(not_(User.username == 'admin')).all()

# 模糊查询(LIKE)
users = User.query.filter(User.username.like('%john%')).all()

# 包含查询(IN)
users = User.query.filter(User.username.in_(['alice', 'bob'])).all()

# 为空判断
users = User.query.filter(User.email.is_(None)).all()

排序与分页

复制代码
# 升序
users = User.query.order_by(User.username).all()

# 降序
users = User.query.order_by(User.username.desc()).all()

# 多字段排序
users = User.query.order_by(User.age.desc(), User.username).all()

# 分页查询(每页10条,第1页)
page = User.query.paginate(page=1, per_page=10)
users = page.items          # 当前页的数据
total = page.total          # 总记录数
pages = page.pages          # 总页数
has_prev = page.has_prev    # 是否有上一页
has_next = page.has_next    # 是否有下一页

聚合查询

复制代码
from sqlalchemy import func

# 计数
count = User.query.count()

# 求和
total_age = db.session.query(func.sum(User.age)).scalar()

# 平均值
avg_age = db.session.query(func.avg(User.age)).scalar()

# 最大值/最小值
max_age = db.session.query(func.max(User.age)).scalar()
min_age = db.session.query(func.min(User.age)).scalar()

6. 使用Flask-Migrate进行数据库迁移

当模型发生变化时(如新增字段),Flask-Migrate可以生成迁移脚本,自动更新表结构而不丢失数据。

安装Flask-Migrate

复制代码
pip install flask-migrate

配置Flask-Migrate

复制代码
from flask_migrate import Migrate

migrate = Migrate(app, db)

迁移命令

复制代码
# 1. 初始化迁移环境(只需执行一次)
flask db init

# 2. 生成迁移脚本(检测模型变化)
flask db migrate -m "添加age字段"

# 3. 应用迁移到数据库
flask db upgrade

# 4. 回滚到上一个版本
flask db downgrade
命令 作用
flask db init 初始化迁移环境,生成migrations/目录
flask db migrate -m "描述" 生成迁移脚本(不修改数据库)
flask db upgrade 应用迁移,更新数据库结构
flask db downgrade 回滚到上一个迁移版本

7. 执行原始SQL

虽然SQLAlchemy提供了强大的ORM功能,但在某些复杂场景下可能需要执行原始SQL语句。

复制代码
# 查询
@app.route('/raw_sql')
def raw_sql():
    result = db.session.execute('SELECT * FROM user')
    return '<br>'.join([str(row) for row in result])

# 带参数的查询(防止SQL注入)
@app.route('/user/<int:user_id>')
def get_user_raw(user_id):
    result = db.session.execute(
        'SELECT * FROM user WHERE id = :id',
        {'id': user_id}
    )
    user = result.fetchone()
    return f'User: {user.username}' if user else 'Not found'

8. 连接MySQL数据库

除了SQLite,Flask-SQLAlchemy也支持MySQL、PostgreSQL等主流数据库。

安装MySQL驱动

复制代码
# 选择一种安装
pip install mysqlclient    # 推荐,性能好
# 或
pip install pymysql        # 纯Python实现

配置MySQL连接

复制代码
# URI格式: mysql://用户名:密码@主机:端口/数据库名
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://username:password@localhost:3306/dbname'
# 如需指定字符集
# app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://username:password@localhost/dbname?charset=utf8mb4'

完整示例

复制代码
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:123456@localhost/mydb'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

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)

@app.route('/')
def index():
    users = User.query.all()
    return '<br>'.join([f'{user.username} ({user.email})' for user in users])

if __name__ == '__main__':
    with app.app_context():
        db.create_all()
    app.run(debug=True)

9. 常用数据库URI格式

数据库 URI格式
SQLite sqlite:///path/to/database.db
SQLite(内存) sqlite:///:memory:
MySQL mysql://username:password@host/dbname
PostgreSQL postgresql://username:password@host/dbname
MariaDB mariadb://username:password@host/dbname

10. CRUD操作速查表

操作 代码
创建记录 db.session.add(obj); db.session.commit()
批量创建 db.session.add_all([obj1, obj2]); db.session.commit()
查询所有 Model.query.all()
主键查询 Model.query.get(id)
条件查询(单条) Model.query.filter_by(key=value).first()
条件查询(多条) Model.query.filter_by(key=value).all()
复杂条件 Model.query.filter(or_(...)).all()
排序 Model.query.order_by(Model.field.desc()).all()
分页 Model.query.paginate(page=1, per_page=10)
更新 obj.field = new_value; db.session.commit()
删除 db.session.delete(obj); db.session.commit()
计数 Model.query.count()

小结

本章全面讲解了Flask的数据库操作。Flask-SQLAlchemy是推荐的ORM方案------通过定义继承自db.Model的模型类来映射数据库表,使用db.Column定义字段;CRUD操作通过db.session.add()创建、Model.query查询、直接修改属性后commit()更新、db.session.delete()删除;高级查询支持filter/filter_by条件过滤、or_/and_组合条件、order_by排序、paginate分页;Flask-Migrate管理数据库迁移;也支持执行原始SQL和连接MySQL等主流数据库。这些知识构成了Flask数据持久化的完整基础。

相关推荐
程序员夏洛22 分钟前
Redis 的 Red Lock 是什么?你了解吗?
数据库·redis·缓存
程序员夏洛27 分钟前
Redis 实现分布式锁时可能遇到的问题有哪些?
数据库·redis·分布式
Sagittarius_A*31 分钟前
【好靶场】报错注入-sql注入-数字型
数据库·sql·web安全·sql注入
Wang's Blog34 分钟前
Vibe Coding一人即团队系列30: AI辅助数据库脚本设计与字符集问题修复
数据库·人工智能
可乐鸡翅yeah_35 分钟前
HTTPS 站点下 M3U8 混合内容问题深度排查,HLS 协议安全策略踩坑实录
数据库·网络协议·https·m3u8·m3u8在线
疯狂打码的少年7 小时前
【数据库技术】关系模型基本概念(关系/属性/元组/键)
java·服务器·数据库·笔记
普马萨特9 小时前
实体与位置关系的数据从哪里来:获取、治理与仍待解决的问题
网络·数据库
醉颜凉9 小时前
MySQL 8 忘记 root 密码?这两种方法帮你轻松解决
数据库·mysql·adb
RestCloud9 小时前
集成链路监控体系搭建:接口调用、数据流转与异常告警全覆盖
数据库·数据安全·ipaas·api管理·数据监控·集成平台·api 治理