Flask 教程

Flask 数据库操作

在 Flask 中,最主流的数据库操作方式是使用 ORM(对象关系映射)。它将数据库中的“表”映射为 Python 的“类”,将“行”映射为“对象”。这样你就可以用 Python 代码而不是 SQL 语句来操作数据库。

业界标准是 Flask-SQLAlchemy,它是 SQLAlchemy 的 Flask 扩展。


🛠️ 1. 安装与配置

纯文本
plaintext
pip install flask-sqlalchemy

基础配置 (app/__init__.pyconfig.py)

纯文本
plaintext
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# 配置数据库 URI (这里以 SQLite 为例,生产环境常用 MySQL 或 PostgreSQL)
# SQLite: sqlite:///database.db
# MySQL: mysql+pymysql://user:password@localhost/db_name
# PostgreSQL: postgresql://user:password@localhost/db_name
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False  # 关闭不必要的信号追踪,节省内存

# 初始化数据库实例
db = SQLAlchemy(app)

🏗️ 2. 定义模型 (Model)

模型是一个 Python 类,继承自 db.Model。类的属性对应数据库的列。

纯文本
plaintext
# models.py
from app import db
from datetime import datetime

class User(db.Model):
    __tablename__ = 'users'  # 可选:指定表名,默认是类名小写

    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)
    password_hash = db.Column(db.String(256))
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    # 建立一对多关系:一个用户可以有多篇文章
    # backref='author' 允许通过 Post.author 访问用户对象
    posts = db.relationship('Post', backref='author', lazy=True)

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

class Post(db.Model):
    __tablename__ = 'posts'

    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(200), nullable=False)
    content = db.Column(db.Text, nullable=False)
    date_posted = db.Column(db.DateTime, default=datetime.utcnow)

    # 外键:关联到 users 表的 id
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)

    def __repr__(self):
        return f'<Post {self.title}>'

常用字段类型:

  • db.Integer: 整数
  • db.String(n): 变长字符串
  • db.Text: 长文本
  • db.Float: 浮点数
  • db.Boolean: 布尔值
  • db.DateTime: 日期时间

常用约束:

  • primary_key=True: 主键
  • unique=True: 唯一索引
  • nullable=False: 不允许为空
  • default=value: 默认值

🔄 3. 数据库迁移 (Flask-Migrate)

千万不要手动修改数据库结构! 当你的 Model 发生变化时(如添加新字段),需要使用迁移工具自动更新数据库表结构。

安装

纯文本
plaintext
pip install flask-migrate

初始化

app/__init__.py 中集成:

纯文本
plaintext
from flask_migrate import Migrate

migrate = Migrate(app, db)

常用命令

在项目根目录下执行:

  1. 初始化迁移文件夹(只需执行一次):
纯文本
plaintext
   flask db init
  1. 生成迁移脚本(每次修改 Model 后执行):
纯文本
plaintext
   flask db migrate -m "添加用户邮箱字段"
  1. 应用迁移到数据库
纯文本
plaintext
   flask db upgrade
  1. 回滚迁移(如果出错了):
纯文本
plaintext
   flask db downgrade

💻 4. CRUD 操作实战

A. 创建 (Create)

纯文本
plaintext
from app import db
from app.models import User, Post

# 创建新用户
new_user = User(username='john', email='john@example.com')
db.session.add(new_user)
db.session.commit()  # 必须提交才能写入数据库

# 创建文章并关联用户
new_post = Post(title='Hello Flask', content='This is my first post.', author=new_user)
db.session.add(new_post)
db.session.commit()

B. 查询 (Read)

SQLAlchemy 提供了强大的查询接口。

纯文本
plaintext
# 1. 查询所有用户
all_users = User.query.all()

# 2. 根据主键查询
user = User.query.get(1)

# 3. 过滤查询 (Filter)
# SELECT * FROM users WHERE username = 'john'
user_john = User.query.filter_by(username='john').first()

# 复杂过滤
# SELECT * FROM users WHERE username LIKE '%j%'
users_like_j = User.query.filter(User.username.like('%j%')).all()

# 4. 排序
posts = Post.query.order_by(Post.date_posted.desc()).all()

# 5. 分页 (Web 开发最常用)
# 获取第 1 页,每页 10 条
page_obj = Post.query.paginate(page=1, per_page=10)
posts_on_page = page_obj.items
total_pages = page_obj.pages

C. 更新 (Update)

纯文本
plaintext
user = User.query.get(1)
user.email = 'new_email@example.com'
db.session.commit()

D. 删除 (Delete)

纯文本
plaintext
user = User.query.get(1)
db.session.delete(user)
db.session.commit()

🔗 5. 关系查询 (Relationships)

利用我们在 Model 中定义的 relationship,可以非常方便地进行关联查询。

纯文本
plaintext
# 1. 获取某个用户的所有文章
user = User.query.get(1)
for post in user.posts:
    print(post.title)

# 2. 获取某篇文章的作者
post = Post.query.get(1)
print(post.author.username)

# 3. 联合查询 (Join)
# 查询所有标题包含 "Flask" 的文章及其作者名
results = db.session.query(Post.title, User.username)\
    .join(User, Post.user_id == User.id)\
    .filter(Post.title.like('%Flask%'))\
    .all()

⚠️ 常见陷阱与最佳实践

  1. 记得 commit:所有的增删改操作最后都必须调用 db.session.commit(),否则数据不会持久化。
  2. 事务回滚:如果发生异常,建议回滚事务以保持数据一致性。
纯文本
plaintext
   try:
       db.session.add(some_object)
       db.session.commit()
   except Exception as e:
       db.session.rollback()
       raise e
  1. N+1 查询问题:在循环中访问关联对象会导致大量数据库查询。
    错误示例
纯文本
plaintext
   posts = Post.query.all()
   for post in posts:
       print(post.author.username) # 每次循环都查一次数据库!

优化:使用 joinedload 预加载。

纯文本
plaintext
   from sqlalchemy.orm import joinedload
   posts = Post.query.options(joinedload(Post.author)).all()
  1. 不要在生产环境使用 SQLite:SQLite 是文件型数据库,并发性能差。生产环境请使用 PostgreSQLMySQL

📝 总结流程图

纯文本
plaintext
定义 Model (Python 类) 
      ↓
flask db migrate (生成迁移脚本)
      ↓
flask db upgrade (更新数据库表结构)
      ↓
视图函数中:
   db.session.add(obj) -> 新增
   Model.query.filter(...) -> 查询
   obj.field = value -> 修改
   db.session.delete(obj) -> 删除
      ↓
db.session.commit() (提交事务)

掌握了数据库操作,你的 Flask 应用就拥有了“记忆”。接下来,你想学习如何结合 Flask-Login 实现完整的用户认证系统,还是学习如何构建 RESTful API

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注