Python 现代开发学习笔记

(七)把对象字段映射为数据库表

1. 从一条记录看 ORM

保存一本书需要执行 INSERT,再用 SELECT 读回。SQLAlchemy 的 ORM 把表记录映射为 Python 对象:Book 类描述表,Book(...) 创建待保存对象,查询取得的 book.title 对应记录中的 title 字段。SQL、索引和事务仍然存在,可以按上一章的方式观察数据库结果。

# 项目文件:api_project/src/bookapi/models.py
from sqlalchemy import (
    JSON,
    Boolean,
    CheckConstraint,
    ForeignKey,
    String,
    UniqueConstraint,
)
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column


class Base(DeclarativeBase):
    # 所有表共享这份 metadata;建表时会统一读取它。
    pass


class User(Base):
    __tablename__ = "users"
    id: Mapped[int] = mapped_column(primary_key=True)
    username: Mapped[str] = mapped_column(String(30), unique=True)
    password_hash: Mapped[str] = mapped_column(String(512))


class Book(Base):
    __tablename__ = "books"
    __table_args__ = (
        UniqueConstraint("owner_id", "title"),
        CheckConstraint("length(title) BETWEEN 1 AND 100"),
    )
    id: Mapped[int] = mapped_column(primary_key=True)
    owner_id: Mapped[int] = mapped_column(ForeignKey("users.id"), index=True)
    title: Mapped[str] = mapped_column(String(100))
    finished: Mapped[bool] = mapped_column(Boolean, default=False)
    tags: Mapped[list[str]] = mapped_column(JSON, default=list)

DeclarativeBase 提供声明映射的基础能力,所有表类继承同一份 Base。__tablename__ 指定表名。Mapped[int] 表示这个属性参与 ORM 映射,Python 端按整数使用;mapped_column 再声明主键、数据库类型、外键或默认值。

ForeignKey("users.id") 关联用户编号,index=True 为书籍归属建立索引。UniqueConstraint("owner_id", "title") 检查组合:alice 可以保存一本《Python 入门》,bob 也可以保存同名书;alice 再存同名书会发生冲突。

JSON 负责把 Python 标签列表转换为数据库可保存的 JSON,再在读取时恢复。修改标签时直接赋一份新列表,如 book.tags = [],ORM 会检测属性被替换并更新记录。原地 book.tags.append(...) 涉及 JSON 内部变化,需要额外的可变类型跟踪;本项目统一使用字段替换。

从两个用户各自保存同名书出发,比较 owner_id 与 title 组合,再把对象字段写入对应表列并关联用户编号

数据库的唯一键负责处理两次实际写入之间的冲突;输入模型负责在进入数据库前给出清楚的字段错误。两者检查的位置不同。可以对照 SQLAlchemy ORM 快速入门 查看映射与查询。

笔记正文与全部题解 · Ctrl / ⌘ K