跳到内容

自动 ID、None 默认值和刷新数据

在上一章节中,我们学习了如何使用 SQLModel 向数据库添加行。

现在让我们稍微讨论一下为什么 id 字段在数据库中不能为 NULL,因为它是一个主键,并且我们使用 Field(primary_key=True) 声明它。

但是同一个 id 字段在 Python 代码中实际上可以是 None,所以我们使用 int | None (或 Optional[int]) 声明类型,并将默认值设置为 Field(default=None)

# Code above omitted 👆

class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: int | None = None

# Code below omitted 👇
👀 完整文件预览
from sqlmodel import Field, Session, SQLModel, create_engine


class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: int | None = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    print("Before interacting with the database")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        print("After adding to the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        session.commit()

        print("After committing the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        print("After committing the session, show IDs")
        print("Hero 1 ID:", hero_1.id)
        print("Hero 2 ID:", hero_2.id)
        print("Hero 3 ID:", hero_3.id)

        print("After committing the session, show names")
        print("Hero 1 name:", hero_1.name)
        print("Hero 2 name:", hero_2.name)
        print("Hero 3 name:", hero_3.name)

        session.refresh(hero_1)
        session.refresh(hero_2)
        session.refresh(hero_3)

        print("After refreshing the heroes")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

    print("After the session closes")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()
🤓 其他版本和变体
from typing import Optional

from sqlmodel import Field, Session, SQLModel, create_engine


class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: Optional[int] = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    print("Before interacting with the database")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        print("After adding to the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        session.commit()

        print("After committing the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        print("After committing the session, show IDs")
        print("Hero 1 ID:", hero_1.id)
        print("Hero 2 ID:", hero_2.id)
        print("Hero 3 ID:", hero_3.id)

        print("After committing the session, show names")
        print("Hero 1 name:", hero_1.name)
        print("Hero 2 name:", hero_2.name)
        print("Hero 3 name:", hero_3.name)

        session.refresh(hero_1)
        session.refresh(hero_2)
        session.refresh(hero_3)

        print("After refreshing the heroes")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

    print("After the session closes")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()

接下来,我将向您展示更多关于数据库和 Python 代码之间数据同步的信息。

我们什么时候才能从数据库中的 id 字段获得实际的 int 值? 让我们看看这一切。 👇

创建一个新的 Hero 实例

当我们创建一个新的 Hero 实例时,我们不设置 id

# Code above omitted 👆

def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

# Code below omitted 👇
👀 完整文件预览
from sqlmodel import Field, Session, SQLModel, create_engine


class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: int | None = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    print("Before interacting with the database")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        print("After adding to the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        session.commit()

        print("After committing the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        print("After committing the session, show IDs")
        print("Hero 1 ID:", hero_1.id)
        print("Hero 2 ID:", hero_2.id)
        print("Hero 3 ID:", hero_3.id)

        print("After committing the session, show names")
        print("Hero 1 name:", hero_1.name)
        print("Hero 2 name:", hero_2.name)
        print("Hero 3 name:", hero_3.name)

        session.refresh(hero_1)
        session.refresh(hero_2)
        session.refresh(hero_3)

        print("After refreshing the heroes")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

    print("After the session closes")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()
🤓 其他版本和变体
from typing import Optional

from sqlmodel import Field, Session, SQLModel, create_engine


class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: Optional[int] = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    print("Before interacting with the database")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        print("After adding to the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        session.commit()

        print("After committing the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        print("After committing the session, show IDs")
        print("Hero 1 ID:", hero_1.id)
        print("Hero 2 ID:", hero_2.id)
        print("Hero 3 ID:", hero_3.id)

        print("After committing the session, show names")
        print("Hero 1 name:", hero_1.name)
        print("Hero 2 name:", hero_2.name)
        print("Hero 3 name:", hero_3.name)

        session.refresh(hero_1)
        session.refresh(hero_2)
        session.refresh(hero_3)

        print("After refreshing the heroes")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

    print("After the session closes")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()

Optional 如何帮助

因为我们没有设置 id,它会采用我们在 Field(default=None) 中设置的 Python 默认值 None

这是我们使用 Optional 并设置默认值 None 的唯一原因。

因为在代码的这一点上,在与数据库交互之前,Python 值实际上可能是 None

如果我们假设 id 始终是一个 int,并且在没有 Optional 的情况下添加类型注解,我们最终可能会编写出错误的代码,例如

next_hero_id = hero_1.id + 1

如果我们在将 hero 保存到数据库之前运行此代码,并且 hero_1.id 仍然是 None,我们会收到如下错误

TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'

但是通过使用 Optional[int] 声明它,编辑器将通过向我们显示警告来帮助我们避免编写错误的代码,告诉我们如果 hero_1.idNone,则代码可能无效。 🔍

我们可以通过在将 heroes 添加到数据库之前打印它们来确认这一点

# Code above omitted 👆

def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    print("Before interacting with the database")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)

# Code below omitted 👇
👀 完整文件预览
from sqlmodel import Field, Session, SQLModel, create_engine


class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: int | None = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    print("Before interacting with the database")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        print("After adding to the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        session.commit()

        print("After committing the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        print("After committing the session, show IDs")
        print("Hero 1 ID:", hero_1.id)
        print("Hero 2 ID:", hero_2.id)
        print("Hero 3 ID:", hero_3.id)

        print("After committing the session, show names")
        print("Hero 1 name:", hero_1.name)
        print("Hero 2 name:", hero_2.name)
        print("Hero 3 name:", hero_3.name)

        session.refresh(hero_1)
        session.refresh(hero_2)
        session.refresh(hero_3)

        print("After refreshing the heroes")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

    print("After the session closes")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()
🤓 其他版本和变体
from typing import Optional

from sqlmodel import Field, Session, SQLModel, create_engine


class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: Optional[int] = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    print("Before interacting with the database")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        print("After adding to the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        session.commit()

        print("After committing the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        print("After committing the session, show IDs")
        print("Hero 1 ID:", hero_1.id)
        print("Hero 2 ID:", hero_2.id)
        print("Hero 3 ID:", hero_3.id)

        print("After committing the session, show names")
        print("Hero 1 name:", hero_1.name)
        print("Hero 2 name:", hero_2.name)
        print("Hero 3 name:", hero_3.name)

        session.refresh(hero_1)
        session.refresh(hero_2)
        session.refresh(hero_3)

        print("After refreshing the heroes")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

    print("After the session closes")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()

这将输出

$ python app.py

// Output above omitted 👆

Before interacting with the database
Hero 1: id=None name='Deadpond' secret_name='Dive Wilson' age=None
Hero 2: id=None name='Spider-Boy' secret_name='Pedro Parqueador' age=None
Hero 3: id=None name='Rusty-Man' secret_name='Tommy Sharp' age=48

请注意,它们都具有 id=None

这是我们在 Hero 模型类中定义的默认值。

当我们把这些对象 add 到会话中时会发生什么?

将对象添加到会话

在我们把 Hero 实例对象添加到会话之后,ID 仍然是 None。

我们可以通过使用 with 块创建一个会话并添加对象来验证。 然后再次打印它们

# Code above omitted 👆

def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    print("Before interacting with the database")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        print("After adding to the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

# Code below omitted 👇
👀 完整文件预览
from sqlmodel import Field, Session, SQLModel, create_engine


class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: int | None = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    print("Before interacting with the database")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        print("After adding to the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        session.commit()

        print("After committing the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        print("After committing the session, show IDs")
        print("Hero 1 ID:", hero_1.id)
        print("Hero 2 ID:", hero_2.id)
        print("Hero 3 ID:", hero_3.id)

        print("After committing the session, show names")
        print("Hero 1 name:", hero_1.name)
        print("Hero 2 name:", hero_2.name)
        print("Hero 3 name:", hero_3.name)

        session.refresh(hero_1)
        session.refresh(hero_2)
        session.refresh(hero_3)

        print("After refreshing the heroes")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

    print("After the session closes")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()
🤓 其他版本和变体
from typing import Optional

from sqlmodel import Field, Session, SQLModel, create_engine


class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: Optional[int] = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    print("Before interacting with the database")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        print("After adding to the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        session.commit()

        print("After committing the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        print("After committing the session, show IDs")
        print("Hero 1 ID:", hero_1.id)
        print("Hero 2 ID:", hero_2.id)
        print("Hero 3 ID:", hero_3.id)

        print("After committing the session, show names")
        print("Hero 1 name:", hero_1.name)
        print("Hero 2 name:", hero_2.name)
        print("Hero 3 name:", hero_3.name)

        session.refresh(hero_1)
        session.refresh(hero_2)
        session.refresh(hero_3)

        print("After refreshing the heroes")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

    print("After the session closes")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()

这将再次将对象的 id 输出为 None

$ python app.py

// Output above omitted 👆

After adding to the session
Hero 1: id=None name='Deadpond' secret_name='Dive Wilson' age=None
Hero 2: id=None name='Spider-Boy' secret_name='Pedro Parqueador' age=None
Hero 3: id=None name='Rusty-Man' secret_name='Tommy Sharp' age=48

正如我们之前看到的,会话是智能的,并且不会在我们每次准备更改某些内容时都与数据库对话,只有在我们准备好并告诉它 commit 更改后,它才会去并将所有 SQL 发送到数据库以存储数据。

将更改提交到数据库

然后我们可以在会话中 commit 更改,并再次打印

# Code above omitted 👆

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        print("After adding to the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        session.commit()

        print("After committing the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

# Code below omitted 👇
👀 完整文件预览
from sqlmodel import Field, Session, SQLModel, create_engine


class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: int | None = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    print("Before interacting with the database")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        print("After adding to the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        session.commit()

        print("After committing the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        print("After committing the session, show IDs")
        print("Hero 1 ID:", hero_1.id)
        print("Hero 2 ID:", hero_2.id)
        print("Hero 3 ID:", hero_3.id)

        print("After committing the session, show names")
        print("Hero 1 name:", hero_1.name)
        print("Hero 2 name:", hero_2.name)
        print("Hero 3 name:", hero_3.name)

        session.refresh(hero_1)
        session.refresh(hero_2)
        session.refresh(hero_3)

        print("After refreshing the heroes")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

    print("After the session closes")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()
🤓 其他版本和变体
from typing import Optional

from sqlmodel import Field, Session, SQLModel, create_engine


class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: Optional[int] = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    print("Before interacting with the database")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        print("After adding to the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        session.commit()

        print("After committing the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        print("After committing the session, show IDs")
        print("Hero 1 ID:", hero_1.id)
        print("Hero 2 ID:", hero_2.id)
        print("Hero 3 ID:", hero_3.id)

        print("After committing the session, show names")
        print("Hero 1 name:", hero_1.name)
        print("Hero 2 name:", hero_2.name)
        print("Hero 3 name:", hero_3.name)

        session.refresh(hero_1)
        session.refresh(hero_2)
        session.refresh(hero_3)

        print("After refreshing the heroes")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

    print("After the session closes")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()

现在,发生了一些意想不到的事情,看看输出,似乎 Hero 实例对象根本没有数据

$ python app.py

// Output above omitted 👆

// Here the engine talks to the database, the SQL part
INFO Engine BEGIN (implicit)
INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
INFO Engine [generated in 0.00018s] ('Deadpond', 'Dive Wilson', None)
INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
INFO Engine [cached since 0.0008968s ago] ('Spider-Boy', 'Pedro Parqueador', None)
INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
INFO Engine [cached since 0.001143s ago] ('Rusty-Man', 'Tommy Sharp', 48)
INFO Engine COMMIT

// And now our prints
After committing the session
Hero 1:
Hero 2:
Hero 3:

// What is happening here? 😱

发生的情况是 SQLModel(实际上是 SQLAlchemy)在内部将这些对象标记为“已过期”,它们没有最新版本的数据。 这是因为我们可能在数据库中更新了一些字段,例如,想象一下一个 updated_at: datetime 字段,它在我们保存更改时自动更新。

同样,其他值也可能已更改,因此会话为确保安全而采取的选项是仅在内部将对象标记为已过期。

然后,下次我们访问每个属性时,例如使用

current_hero_name = hero_1.name

...SQLModel(实际上是 SQLAlchemy)将确保联系数据库并获取最新版本的数据,更新我们对象中的 name 字段,然后使其可用于 Python 表达式的其余部分。 在上面的示例中,在那一点上,Python 将能够继续执行并使用该 hero_1.name 值(刚刚更新)将其放入变量 current_hero_name 中。

所有这一切都自动发生在幕后。 ✨

这是我们示例中一件有趣而奇怪的事情

print("Hero 1:", hero_1)

我们没有访问对象的属性,例如 hero.name。 我们只访问了整个对象并打印了它,因此 SQLAlchemy 无法知道我们想要访问此对象的数据。

为了确认和理解当访问属性时,这种数据的自动过期和刷新是如何工作的,我们可以打印一些单独的字段(实例属性)

# Code above omitted 👆

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        print("After adding to the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        session.commit()

        print("After committing the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        print("After committing the session, show IDs")
        print("Hero 1 ID:", hero_1.id)
        print("Hero 2 ID:", hero_2.id)
        print("Hero 3 ID:", hero_3.id)

        print("After committing the session, show names")
        print("Hero 1 name:", hero_1.name)
        print("Hero 2 name:", hero_2.name)
        print("Hero 3 name:", hero_3.name)

# Code below omitted 👇
👀 完整文件预览
from sqlmodel import Field, Session, SQLModel, create_engine


class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: int | None = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    print("Before interacting with the database")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        print("After adding to the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        session.commit()

        print("After committing the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        print("After committing the session, show IDs")
        print("Hero 1 ID:", hero_1.id)
        print("Hero 2 ID:", hero_2.id)
        print("Hero 3 ID:", hero_3.id)

        print("After committing the session, show names")
        print("Hero 1 name:", hero_1.name)
        print("Hero 2 name:", hero_2.name)
        print("Hero 3 name:", hero_3.name)

        session.refresh(hero_1)
        session.refresh(hero_2)
        session.refresh(hero_3)

        print("After refreshing the heroes")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

    print("After the session closes")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()
🤓 其他版本和变体
from typing import Optional

from sqlmodel import Field, Session, SQLModel, create_engine


class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: Optional[int] = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    print("Before interacting with the database")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        print("After adding to the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        session.commit()

        print("After committing the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        print("After committing the session, show IDs")
        print("Hero 1 ID:", hero_1.id)
        print("Hero 2 ID:", hero_2.id)
        print("Hero 3 ID:", hero_3.id)

        print("After committing the session, show names")
        print("Hero 1 name:", hero_1.name)
        print("Hero 2 name:", hero_2.name)
        print("Hero 3 name:", hero_3.name)

        session.refresh(hero_1)
        session.refresh(hero_2)
        session.refresh(hero_3)

        print("After refreshing the heroes")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

    print("After the session closes")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()

现在我们实际上是在访问属性,因为我们没有打印整个对象 hero_1

print("Hero 1:", hero_1)

...我们现在正在打印 hero.id 中的 id 属性

print("Hero 1 ID:", hero_1.id)

通过访问属性,这会触发 SQLModel(实际上是 SQLAlchemy)在底层完成大量工作,以从数据库刷新数据,将其设置在对象的 id 属性中,并使其可用于 Python 表达式(在本例中只是打印它)。

让我们看看它是如何工作的

$ python app.py

// Output above omitted 👆

// After committing, the objects are expired and have no values
After committing the session
Hero 1:
Hero 2:
Hero 3:

// Now we will access an attribute like the ID, this is the first print
After committing the session, show IDs

// Notice that before printing the first ID, the Session makes the Engine go to the database to refresh the data 🤓
INFO Engine BEGIN (implicit)
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
WHERE hero.id = ?
INFO Engine [generated in 0.00017s] (1,)

// Here's our first print, now we have the database-generated ID
Hero 1 ID: 1

// Before the next print, refresh the data for the second object
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.001245s ago] (2,)

// Here's our print for the second hero with its auto-generated ID
Hero 2 ID: 2

// Before the third print, refresh its data
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.002215s ago] (3,)

// And here's our print for the third hero
Hero 3 ID: 3

// What if we print another attribute like the name?
After committing the session, show names
Hero 1 name: Deadpond
Hero 2 name: Spider-Boy
Hero 3 name: Rusty-Man

// Because the Session already refreshed these objects with all their data and the session knows they are not expired, it doesn't have to go again to the database for the names 🤓

显式刷新对象

您刚刚学习了当您访问属性时,会话如何在幕后自动刷新数据,作为副作用。

但是,如果您想显式刷新数据怎么办?

您也可以使用 session.refresh(object) 来做到这一点

# Code above omitted 👆

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        print("After adding to the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        session.commit()

        print("After committing the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        print("After committing the session, show IDs")
        print("Hero 1 ID:", hero_1.id)
        print("Hero 2 ID:", hero_2.id)
        print("Hero 3 ID:", hero_3.id)

        print("After committing the session, show names")
        print("Hero 1 name:", hero_1.name)
        print("Hero 2 name:", hero_2.name)
        print("Hero 3 name:", hero_3.name)

        session.refresh(hero_1)
        session.refresh(hero_2)
        session.refresh(hero_3)

        print("After refreshing the heroes")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

# Code below omitted 👇
👀 完整文件预览
from sqlmodel import Field, Session, SQLModel, create_engine


class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: int | None = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    print("Before interacting with the database")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        print("After adding to the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        session.commit()

        print("After committing the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        print("After committing the session, show IDs")
        print("Hero 1 ID:", hero_1.id)
        print("Hero 2 ID:", hero_2.id)
        print("Hero 3 ID:", hero_3.id)

        print("After committing the session, show names")
        print("Hero 1 name:", hero_1.name)
        print("Hero 2 name:", hero_2.name)
        print("Hero 3 name:", hero_3.name)

        session.refresh(hero_1)
        session.refresh(hero_2)
        session.refresh(hero_3)

        print("After refreshing the heroes")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

    print("After the session closes")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()
🤓 其他版本和变体
from typing import Optional

from sqlmodel import Field, Session, SQLModel, create_engine


class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: Optional[int] = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    print("Before interacting with the database")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        print("After adding to the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        session.commit()

        print("After committing the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        print("After committing the session, show IDs")
        print("Hero 1 ID:", hero_1.id)
        print("Hero 2 ID:", hero_2.id)
        print("Hero 3 ID:", hero_3.id)

        print("After committing the session, show names")
        print("Hero 1 name:", hero_1.name)
        print("Hero 2 name:", hero_2.name)
        print("Hero 3 name:", hero_3.name)

        session.refresh(hero_1)
        session.refresh(hero_2)
        session.refresh(hero_3)

        print("After refreshing the heroes")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

    print("After the session closes")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()

当 Python 执行此代码时

session.refresh(hero_1)

...会话会去让引擎与数据库通信,以获取此对象 hero_1 的最新数据,然后会话将数据放入 hero_1 对象中,并将其标记为“新鲜”或“未过期”。

这是输出的样子

$ python app.py

// Output above omitted 👆

// The first refresh
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
WHERE hero.id = ?
INFO Engine [generated in 0.00024s] (1,)

// The second refresh
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.001487s ago] (2,)

// The third refresh
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.002377s ago] (3,)

// Now print the data, as it's already refreshed there's no need for the Session to go and refresh it again
After refreshing the heroes
Hero 1: age=None id=1 name='Deadpond' secret_name='Dive Wilson'
Hero 2: age=None id=2 name='Spider-Boy' secret_name='Pedro Parqueador'
Hero 3: age=48 id=3 name='Rusty-Man' secret_name='Tommy Sharp'

这可能很有用,例如,如果您正在构建一个 Web API 来创建 heroes。 一旦使用某些数据创建了一个 hero,您将其返回给客户端。

您不会希望返回一个看起来为空的对象,因为刷新数据的自动魔术没有被触发。

在这种情况下,在使用会话将对象提交到数据库后,您可以刷新它,然后将其返回给客户端。 这将确保对象具有其新鲜数据。

现在,作为最后一个实验,我们还可以在会话关闭后打印数据。

这里没有惊喜,它仍然有效

# Code above omitted 👆

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        print("After adding to the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        session.commit()

        print("After committing the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        print("After committing the session, show IDs")
        print("Hero 1 ID:", hero_1.id)
        print("Hero 2 ID:", hero_2.id)
        print("Hero 3 ID:", hero_3.id)

        print("After committing the session, show names")
        print("Hero 1 name:", hero_1.name)
        print("Hero 2 name:", hero_2.name)
        print("Hero 3 name:", hero_3.name)

        session.refresh(hero_1)
        session.refresh(hero_2)
        session.refresh(hero_3)

        print("After refreshing the heroes")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

    print("After the session closes")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)

# Code below omitted 👇
👀 完整文件预览
from sqlmodel import Field, Session, SQLModel, create_engine


class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: int | None = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    print("Before interacting with the database")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        print("After adding to the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        session.commit()

        print("After committing the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        print("After committing the session, show IDs")
        print("Hero 1 ID:", hero_1.id)
        print("Hero 2 ID:", hero_2.id)
        print("Hero 3 ID:", hero_3.id)

        print("After committing the session, show names")
        print("Hero 1 name:", hero_1.name)
        print("Hero 2 name:", hero_2.name)
        print("Hero 3 name:", hero_3.name)

        session.refresh(hero_1)
        session.refresh(hero_2)
        session.refresh(hero_3)

        print("After refreshing the heroes")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

    print("After the session closes")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()
🤓 其他版本和变体
from typing import Optional

from sqlmodel import Field, Session, SQLModel, create_engine


class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: Optional[int] = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    print("Before interacting with the database")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        print("After adding to the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        session.commit()

        print("After committing the session")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

        print("After committing the session, show IDs")
        print("Hero 1 ID:", hero_1.id)
        print("Hero 2 ID:", hero_2.id)
        print("Hero 3 ID:", hero_3.id)

        print("After committing the session, show names")
        print("Hero 1 name:", hero_1.name)
        print("Hero 2 name:", hero_2.name)
        print("Hero 3 name:", hero_3.name)

        session.refresh(hero_1)
        session.refresh(hero_2)
        session.refresh(hero_3)

        print("After refreshing the heroes")
        print("Hero 1:", hero_1)
        print("Hero 2:", hero_2)
        print("Hero 3:", hero_3)

    print("After the session closes")
    print("Hero 1:", hero_1)
    print("Hero 2:", hero_2)
    print("Hero 3:", hero_3)


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()

输出再次显示相同的数据

$ python app.py

// Output above omitted 👆

// By finishing the with block, the Session is closed, including a rollback of any pending transaction that could have been there and was not committed
INFO Engine ROLLBACK

// Then we print the data, it works normally
After the session closes
Hero 1: age=None id=1 name='Deadpond' secret_name='Dive Wilson'
Hero 2: age=None id=2 name='Spider-Boy' secret_name='Pedro Parqueador'
Hero 3: age=48 id=3 name='Rusty-Man' secret_name='Tommy Sharp'

回顾所有代码

现在让我们再次回顾所有这些代码。

提示

每个编号的气泡都显示了每一行将在输出中打印的内容。

并且当我们使用 echo=True 创建引擎时,我们可以看到在每个步骤中执行的 SQL 语句。

from sqlmodel import Field, Session, SQLModel, create_engine


class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: int | None = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")  # (1)!
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")  # (2)!
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)  # (3)!

    print("Before interacting with the database")  # (4)!
    print("Hero 1:", hero_1)  # (5)!
    print("Hero 2:", hero_2)  # (6)!
    print("Hero 3:", hero_3)  # (7)!

    with Session(engine) as session:  # (8)!
        session.add(hero_1)  # (9)!
        session.add(hero_2)  # (10)!
        session.add(hero_3)  # (11)!

        print("After adding to the session")  # (12)!
        print("Hero 1:", hero_1)  # (13)!
        print("Hero 2:", hero_2)  # (14)!
        print("Hero 3:", hero_3)  # (15)!

        session.commit()  # (16)!

        print("After committing the session")  # (17)!
        print("Hero 1:", hero_1)  # (18)!
        print("Hero 2:", hero_2)  # (19)!
        print("Hero 3:", hero_3)  # (20)!

        print("After committing the session, show IDs")  # (21)!
        print("Hero 1 ID:", hero_1.id)  # (22)!
        print("Hero 2 ID:", hero_2.id)  # (23)!
        print("Hero 3 ID:", hero_3.id)  # (24)!

        print("After committing the session, show names")  # (25)!
        print("Hero 1 name:", hero_1.name)  # (26)!
        print("Hero 2 name:", hero_2.name)  # (27)!
        print("Hero 3 name:", hero_3.name)  # (28)!

        session.refresh(hero_1)  # (29)!
        session.refresh(hero_2)  # (30)!
        session.refresh(hero_3)  # (31)!

        print("After refreshing the heroes")  # (32)!
        print("Hero 1:", hero_1)  # (33)!
        print("Hero 2:", hero_2)  # (34)!
        print("Hero 3:", hero_3)  # (35)!
    # (36)!

    print("After the session closes")  # (37)!
    print("Hero 1:", hero_1)  # (38)!
    print("Hero 2:", hero_2)  # (39)!
    print("Hero 3:", hero_3)  # (40)!


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()
  1. 创建 hero_1

    不生成任何输出.

  2. 创建 hero_2

    不生成任何输出.

  3. 创建 hero_3

    不生成任何输出.

  4. 打印行 "Before interacting with the database"

    生成输出

    Before interacting with the database
    
  5. 在与数据库交互之前打印 hero_1

    生成输出

    Hero 1: id=None name='Deadpond' secret_name='Dive Wilson' age=None
    
  6. 在与数据库交互之前打印 hero_2

    生成输出

    Hero 2: id=None name='Spider-Boy' secret_name='Pedro Parqueador' age=None
    
  7. 在与数据库交互之前打印 hero_3

    生成输出

    Hero 3: id=None name='Rusty-Man' secret_name='Tommy Sharp' age=48
    
  8. with 块中创建 Session

    不生成任何输出.

  9. hero_1 添加到会话。

    这仍然不会将其保存到数据库。

    不生成任何输出.

  10. hero_2 添加到会话。

    这仍然不会将其保存到数据库。

    不生成任何输出.

  11. hero_3 添加到会话。

    这仍然不会将其保存到数据库。

    不生成任何输出.

  12. 打印行 "After adding to the session"

    生成输出

    After adding to the session
    
  13. 在将其添加到会话后打印 hero_1

    它仍然具有相同的数据,因为尚未与数据库进行任何交互。 请注意,id 仍然是 None

    生成输出

    Hero 1: id=None name='Deadpond' secret_name='Dive Wilson' age=None
    
  14. 在将其添加到会话后打印 hero_2

    它仍然具有相同的数据,因为尚未与数据库进行任何交互。 请注意,id 仍然是 None

    生成输出

    Hero 2: id=None name='Spider-Boy' secret_name='Pedro Parqueador' age=None
    
  15. 在将其添加到会话后打印 hero_3

    它仍然具有相同的数据,因为尚未与数据库进行任何交互。 请注意,id 仍然是 None

    生成输出

    Hero 3: id=None name='Rusty-Man' secret_name='Tommy Sharp' age=48
    
  16. commit 会话。

    这将把所有数据保存到数据库。 会话将使用引擎运行大量 SQL。

    生成输出

    INFO Engine BEGIN (implicit)
    INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
    INFO Engine [generated in 0.00018s] ('Deadpond', 'Dive Wilson', None)
    INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
    INFO Engine [cached since 0.0008968s ago] ('Spider-Boy', 'Pedro Parqueador', None)
    INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
    INFO Engine [cached since 0.001143s ago] ('Rusty-Man', 'Tommy Sharp', 48)
    INFO Engine COMMIT
    
  17. 打印行 "After committing the session"

    生成输出

    After committing the session
    
  18. 在提交会话后打印 hero_1

    hero_1 现在在内部被标记为已过期,并且在刷新之前,它看起来好像不包含任何数据。

    生成输出

    Hero 1:
    
  19. 在提交会话后打印 hero_2

    hero_2 现在在内部被标记为已过期,并且在刷新之前,它看起来好像不包含任何数据。

    生成输出

    Hero 2:
    
  20. 在提交会话后打印 hero_3

    hero_3 现在在内部被标记为已过期,并且在刷新之前,它看起来好像不包含任何数据。

    生成输出

    Hero 3:
    
  21. 打印行 "After committing the session, show IDs"

    生成输出

    After committing the session, show IDs
    
  22. 打印 hero_1.id。 这里发生了很多事情。

    因为我们正在访问 hero_1 的属性 id,所以 SQLModel(实际上是 SQLAlchemy)可以检测到我们正在尝试从 hero_1 访问数据。

    然后它检测到 hero_1 当前与会话关联(因为我们将其添加到会话并提交了它),并且它被标记为已过期。

    然后通过会话,它使用引擎执行所有 SQL,以从数据库中获取此对象的数据。

    接下来,它使用新数据更新对象,并在内部将其标记为“新鲜”或“未过期”。

    最后,它使 ID 值可用于 Python 表达式的其余部分。 在这种情况下,Python 表达式只是打印 ID。

    生成输出

    INFO Engine BEGIN (implicit)
    INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
    FROM hero
    WHERE hero.id = ?
    INFO Engine [generated in 0.00017s] (1,)
    
    Hero 1 ID: 1
    
  23. 打印 hero_2.id

    这里发生了很多事情,与第 22 点发生的事情相同,但这次是针对 hero_2 对象。

    生成输出

    INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
    FROM hero
    WHERE hero.id = ?
    INFO Engine [cached since 0.001245s ago] (2,)
    
    Hero 2 ID: 2
    
  24. 打印 hero_3.id

    这里发生了很多事情,与第 22 点发生的事情相同,但这次是针对 hero_3 对象。

    生成输出

    INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
    FROM hero
    WHERE hero.id = ?
    INFO Engine [cached since 0.002215s ago] (3,)
    
    
    Hero 3 ID: 3
    
  25. 打印行 "After committing the session, show names"

    生成输出

    After committing the session, show names
    
  26. 打印 hero_1.name

    因为 hero_1 仍然是新鲜的,所以不会获取其他数据,也不会执行其他 SQL,并且名称可用。

    生成输出

    Hero 1 name: Deadpond
    
  27. 打印 hero_2.name

    因为 hero_2 仍然是新鲜的,所以不会获取其他数据,也不会执行其他 SQL,并且名称可用。

    生成输出

    Hero 2 name: Spider-Boy
    
  28. 打印 hero_3.name

    因为 hero_3 仍然是新鲜的,所以不会获取其他数据,也不会执行其他 SQL,并且名称可用。

    生成输出

    Hero 3 name: Rusty-Man
    
  29. 显式刷新 hero_1 对象。

    会话将使用引擎来执行必要的 SQL,以从数据库中获取 hero_1 对象的新鲜数据。

    生成输出

    INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
    FROM hero
    WHERE hero.id = ?
    INFO Engine [generated in 0.00024s] (1,)
    
  30. 显式刷新 hero_2 对象。

    会话将使用引擎来执行必要的 SQL,以从数据库中获取 hero_2 对象的新鲜数据。

    生成输出

    INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
    FROM hero
    WHERE hero.id = ?
    INFO Engine [cached since 0.001487s ago] (2,)
    
  31. 显式刷新 hero_3 对象。

    会话将使用引擎来执行必要的 SQL,以从数据库中获取 hero_3 对象的新鲜数据。

    生成输出

    INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
    FROM hero
    WHERE hero.id = ?
    INFO Engine [cached since 0.002377s ago] (3,)
    
  32. 打印 "After refreshing the heroes" 这一行。

    生成输出

    After refreshing the heroes
    
  33. 打印 hero_1

    信息

    即使 hero_1 不是新鲜的,这不会触发 refresh,从而使会话使用引擎从数据库中获取数据,因为它没有访问任何属性。

    因为 hero_1 是新鲜的,所以它拥有所有可用的数据。

    生成输出

    Hero 1: age=None id=1 name='Deadpond' secret_name='Dive Wilson'
    
  34. 打印 hero_2

    信息

    即使 hero_2 不是新鲜的,这不会触发 refresh,从而使会话使用引擎从数据库中获取数据,因为它没有访问任何属性。

    因为 hero_2 是新鲜的,所以它拥有所有可用的数据。

    生成输出

    Hero 2: age=None id=2 name='Spider-Boy' secret_name='Pedro Parqueador'
    
  35. 打印 hero_3

    信息

    即使 hero_3 不是新鲜的,这不会触发 refresh,从而使会话使用引擎从数据库中获取数据,因为它没有访问任何属性。

    因为 hero_3 是新鲜的,所以它拥有所有可用的数据。

    生成输出

    Hero 3: age=48 id=3 name='Rusty-Man' secret_name='Tommy Sharp'
    
  36. with 代码块在此处结束(没有更多缩进的代码),因此会话已关闭,并运行其所有关闭代码。

    这包括对可能已启动的任何事务执行 ROLLBACK(回滚)。

    生成输出

    INFO Engine ROLLBACK
    
  37. 打印 "After the session closes" 这一行。

    生成输出

    After the session closes
    
  38. 在会话关闭后打印 hero_1

    生成输出

    Hero 1: age=None id=1 name='Deadpond' secret_name='Dive Wilson'
    
  39. 在会话关闭后打印 hero_2

    生成输出

    Hero 2: age=None id=2 name='Spider-Boy' secret_name='Pedro Parqueador'
    
  40. 在会话关闭后打印 hero_3

    生成输出

    Hero 3: age=48 id=3 name='Rusty-Man' secret_name='Tommy Sharp'
    
from typing import Optional

from sqlmodel import Field, Session, SQLModel, create_engine


class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: Optional[int] = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")  # (1)!
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")  # (2)!
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)  # (3)!

    print("Before interacting with the database")  # (4)!
    print("Hero 1:", hero_1)  # (5)!
    print("Hero 2:", hero_2)  # (6)!
    print("Hero 3:", hero_3)  # (7)!

    with Session(engine) as session:  # (8)!
        session.add(hero_1)  # (9)!
        session.add(hero_2)  # (10)!
        session.add(hero_3)  # (11)!

        print("After adding to the session")  # (12)!
        print("Hero 1:", hero_1)  # (13)!
        print("Hero 2:", hero_2)  # (14)!
        print("Hero 3:", hero_3)  # (15)!

        session.commit()  # (16)!

        print("After committing the session")  # (17)!
        print("Hero 1:", hero_1)  # (18)!
        print("Hero 2:", hero_2)  # (19)!
        print("Hero 3:", hero_3)  # (20)!

        print("After committing the session, show IDs")  # (21)!
        print("Hero 1 ID:", hero_1.id)  # (22)!
        print("Hero 2 ID:", hero_2.id)  # (23)!
        print("Hero 3 ID:", hero_3.id)  # (24)!

        print("After committing the session, show names")  # (25)!
        print("Hero 1 name:", hero_1.name)  # (26)!
        print("Hero 2 name:", hero_2.name)  # (27)!
        print("Hero 3 name:", hero_3.name)  # (28)!

        session.refresh(hero_1)  # (29)!
        session.refresh(hero_2)  # (30)!
        session.refresh(hero_3)  # (31)!

        print("After refreshing the heroes")  # (32)!
        print("Hero 1:", hero_1)  # (33)!
        print("Hero 2:", hero_2)  # (34)!
        print("Hero 3:", hero_3)  # (35)!
    # (36)!

    print("After the session closes")  # (37)!
    print("Hero 1:", hero_1)  # (38)!
    print("Hero 2:", hero_2)  # (39)!
    print("Hero 3:", hero_3)  # (40)!


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()
  1. 创建 hero_1

    不生成任何输出.

  2. 创建 hero_2

    不生成任何输出.

  3. 创建 hero_3

    不生成任何输出.

  4. 打印行 "Before interacting with the database"

    生成输出

    Before interacting with the database
    
  5. 在与数据库交互之前打印 hero_1

    生成输出

    Hero 1: id=None name='Deadpond' secret_name='Dive Wilson' age=None
    
  6. 在与数据库交互之前打印 hero_2

    生成输出

    Hero 2: id=None name='Spider-Boy' secret_name='Pedro Parqueador' age=None
    
  7. 在与数据库交互之前打印 hero_3

    生成输出

    Hero 3: id=None name='Rusty-Man' secret_name='Tommy Sharp' age=48
    
  8. with 块中创建 Session

    不生成任何输出.

  9. hero_1 添加到会话。

    这仍然不会将其保存到数据库。

    不生成任何输出.

  10. hero_2 添加到会话。

    这仍然不会将其保存到数据库。

    不生成任何输出.

  11. hero_3 添加到会话。

    这仍然不会将其保存到数据库。

    不生成任何输出.

  12. 打印行 "After adding to the session"

    生成输出

    After adding to the session
    
  13. 在将其添加到会话后打印 hero_1

    它仍然具有相同的数据,因为尚未与数据库进行任何交互。 请注意,id 仍然是 None

    生成输出

    Hero 1: id=None name='Deadpond' secret_name='Dive Wilson' age=None
    
  14. 在将其添加到会话后打印 hero_2

    它仍然具有相同的数据,因为尚未与数据库进行任何交互。 请注意,id 仍然是 None

    生成输出

    Hero 2: id=None name='Spider-Boy' secret_name='Pedro Parqueador' age=None
    
  15. 在将其添加到会话后打印 hero_3

    它仍然具有相同的数据,因为尚未与数据库进行任何交互。 请注意,id 仍然是 None

    生成输出

    Hero 3: id=None name='Rusty-Man' secret_name='Tommy Sharp' age=48
    
  16. commit 会话。

    这将把所有数据保存到数据库。 会话将使用引擎运行大量 SQL。

    生成输出

    INFO Engine BEGIN (implicit)
    INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
    INFO Engine [generated in 0.00018s] ('Deadpond', 'Dive Wilson', None)
    INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
    INFO Engine [cached since 0.0008968s ago] ('Spider-Boy', 'Pedro Parqueador', None)
    INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
    INFO Engine [cached since 0.001143s ago] ('Rusty-Man', 'Tommy Sharp', 48)
    INFO Engine COMMIT
    
  17. 打印行 "After committing the session"

    生成输出

    After committing the session
    
  18. 在提交会话后打印 hero_1

    hero_1 现在在内部被标记为已过期,并且在刷新之前,它看起来好像不包含任何数据。

    生成输出

    Hero 1:
    
  19. 在提交会话后打印 hero_2

    hero_2 现在在内部被标记为已过期,并且在刷新之前,它看起来好像不包含任何数据。

    生成输出

    Hero 2:
    
  20. 在提交会话后打印 hero_3

    hero_3 现在在内部被标记为已过期,并且在刷新之前,它看起来好像不包含任何数据。

    生成输出

    Hero 3:
    
  21. 打印行 "After committing the session, show IDs"

    生成输出

    After committing the session, show IDs
    
  22. 打印 hero_1.id。 这里发生了很多事情。

    因为我们正在访问 hero_1 的属性 id,所以 SQLModel(实际上是 SQLAlchemy)可以检测到我们正在尝试从 hero_1 访问数据。

    然后它检测到 hero_1 当前与会话关联(因为我们将其添加到会话并提交了它),并且它被标记为已过期。

    然后通过会话,它使用引擎执行所有 SQL,以从数据库中获取此对象的数据。

    接下来,它使用新数据更新对象,并在内部将其标记为“新鲜”或“未过期”。

    最后,它使 ID 值可用于 Python 表达式的其余部分。 在这种情况下,Python 表达式只是打印 ID。

    生成输出

    INFO Engine BEGIN (implicit)
    INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
    FROM hero
    WHERE hero.id = ?
    INFO Engine [generated in 0.00017s] (1,)
    
    Hero 1 ID: 1
    
  23. 打印 hero_2.id

    这里发生了很多事情,与第 22 点发生的事情相同,但这次是针对 hero_2 对象。

    生成输出

    INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
    FROM hero
    WHERE hero.id = ?
    INFO Engine [cached since 0.001245s ago] (2,)
    
    Hero 2 ID: 2
    
  24. 打印 hero_3.id

    这里发生了很多事情,与第 22 点发生的事情相同,但这次是针对 hero_3 对象。

    生成输出

    INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
    FROM hero
    WHERE hero.id = ?
    INFO Engine [cached since 0.002215s ago] (3,)
    
    
    Hero 3 ID: 3
    
  25. 打印行 "After committing the session, show names"

    生成输出

    After committing the session, show names
    
  26. 打印 hero_1.name

    因为 hero_1 仍然是新鲜的,所以不会获取其他数据,也不会执行其他 SQL,并且名称可用。

    生成输出

    Hero 1 name: Deadpond
    
  27. 打印 hero_2.name

    因为 hero_2 仍然是新鲜的,所以不会获取其他数据,也不会执行其他 SQL,并且名称可用。

    生成输出

    Hero 2 name: Spider-Boy
    
  28. 打印 hero_3.name

    因为 hero_3 仍然是新鲜的,所以不会获取其他数据,也不会执行其他 SQL,并且名称可用。

    生成输出

    Hero 3 name: Rusty-Man
    
  29. 显式刷新 hero_1 对象。

    会话将使用引擎来执行必要的 SQL,以从数据库中获取 hero_1 对象的新鲜数据。

    生成输出

    INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
    FROM hero
    WHERE hero.id = ?
    INFO Engine [generated in 0.00024s] (1,)
    
  30. 显式刷新 hero_2 对象。

    会话将使用引擎来执行必要的 SQL,以从数据库中获取 hero_2 对象的新鲜数据。

    生成输出

    INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
    FROM hero
    WHERE hero.id = ?
    INFO Engine [cached since 0.001487s ago] (2,)
    
  31. 显式刷新 hero_3 对象。

    会话将使用引擎来执行必要的 SQL,以从数据库中获取 hero_3 对象的新鲜数据。

    生成输出

    INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
    FROM hero
    WHERE hero.id = ?
    INFO Engine [cached since 0.002377s ago] (3,)
    
  32. 打印 "After refreshing the heroes" 这一行。

    生成输出

    After refreshing the heroes
    
  33. 打印 hero_1

    信息

    即使 hero_1 不是新鲜的,这不会触发 refresh,从而使会话使用引擎从数据库中获取数据,因为它没有访问任何属性。

    因为 hero_1 是新鲜的,所以它拥有所有可用的数据。

    生成输出

    Hero 1: age=None id=1 name='Deadpond' secret_name='Dive Wilson'
    
  34. 打印 hero_2

    信息

    即使 hero_2 不是新鲜的,这不会触发 refresh,从而使会话使用引擎从数据库中获取数据,因为它没有访问任何属性。

    因为 hero_2 是新鲜的,所以它拥有所有可用的数据。

    生成输出

    Hero 2: age=None id=2 name='Spider-Boy' secret_name='Pedro Parqueador'
    
  35. 打印 hero_3

    信息

    即使 hero_3 不是新鲜的,这不会触发 refresh,从而使会话使用引擎从数据库中获取数据,因为它没有访问任何属性。

    因为 hero_3 是新鲜的,所以它拥有所有可用的数据。

    生成输出

    Hero 3: age=48 id=3 name='Rusty-Man' secret_name='Tommy Sharp'
    
  36. with 代码块在此处结束(没有更多缩进的代码),因此会话已关闭,并运行其所有关闭代码。

    这包括对可能已启动的任何事务执行 ROLLBACK(回滚)。

    生成输出

    INFO Engine ROLLBACK
    
  37. 打印 "After the session closes" 这一行。

    生成输出

    After the session closes
    
  38. 在会话关闭后打印 hero_1

    生成输出

    Hero 1: age=None id=1 name='Deadpond' secret_name='Dive Wilson'
    
  39. 在会话关闭后打印 hero_2

    生成输出

    Hero 2: age=None id=2 name='Spider-Boy' secret_name='Pedro Parqueador'
    
  40. 在会话关闭后打印 hero_3

    生成输出

    Hero 3: age=48 id=3 name='Rusty-Man' secret_name='Tommy Sharp'
    

这是运行此程序生成的所有输出,全部放在一起

$ python app.py

INFO Engine BEGIN (implicit)
INFO Engine PRAGMA main.table_info("hero")
INFO Engine [raw sql] ()
INFO Engine PRAGMA temp.table_info("hero")
INFO Engine [raw sql] ()
INFO Engine
CREATE TABLE hero (
        id INTEGER,
        name VARCHAR NOT NULL,
        secret_name VARCHAR NOT NULL,
        age INTEGER,
        PRIMARY KEY (id)
)


INFO Engine [no key 0.00018s] ()
INFO Engine COMMIT
Before interacting with the database
Hero 1: id=None name='Deadpond' secret_name='Dive Wilson' age=None
Hero 2: id=None name='Spider-Boy' secret_name='Pedro Parqueador' age=None
Hero 3: id=None name='Rusty-Man' secret_name='Tommy Sharp' age=48
After adding to the session
Hero 1: id=None name='Deadpond' secret_name='Dive Wilson' age=None
Hero 2: id=None name='Spider-Boy' secret_name='Pedro Parqueador' age=None
Hero 3: id=None name='Rusty-Man' secret_name='Tommy Sharp' age=48
INFO Engine BEGIN (implicit)
INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
INFO Engine [generated in 0.00022s] ('Deadpond', 'Dive Wilson', None)
INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
INFO Engine [cached since 0.001127s ago] ('Spider-Boy', 'Pedro Parqueador', None)
INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
INFO Engine [cached since 0.001483s ago] ('Rusty-Man', 'Tommy Sharp', 48)
INFO Engine COMMIT
After committing the session
Hero 1:
Hero 2:
Hero 3:
After committing the session, show IDs
INFO Engine BEGIN (implicit)
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
WHERE hero.id = ?
INFO Engine [generated in 0.00029s] (1,)
Hero 1 ID: 1
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.002132s ago] (2,)
Hero 2 ID: 2
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.003367s ago] (3,)
Hero 3 ID: 3
After committing the session, show names
Hero 1 name: Deadpond
Hero 2 name: Spider-Boy
Hero 3 name: Rusty-Man
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
WHERE hero.id = ?
INFO Engine [generated in 0.00025s] (1,)
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.001583s ago] (2,)
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.002722s ago] (3,)
After refreshing the heroes
Hero 1: age=None id=1 name='Deadpond' secret_name='Dive Wilson'
Hero 2: age=None id=2 name='Spider-Boy' secret_name='Pedro Parqueador'
Hero 3: age=48 id=3 name='Rusty-Man' secret_name='Tommy Sharp'
INFO Engine ROLLBACK
After the session closes
Hero 1: age=None id=1 name='Deadpond' secret_name='Dive Wilson'
Hero 2: age=None id=2 name='Spider-Boy' secret_name='Pedro Parqueador'
Hero 3: age=48 id=3 name='Rusty-Man' secret_name='Tommy Sharp'

回顾

你读完了所有内容!真多啊!吃些蛋糕吧,这是你应得的。🍰

我们讨论了会话如何使用引擎向数据库发送 SQL,以创建数据和获取数据。以及它如何跟踪“过期的”和“新鲜的”数据。在哪些时刻它会自动获取数据(当访问实例属性时),以及数据如何通过会话在内存中的对象和数据库之间同步。

如果你理解了所有这些,那么你现在对 SQLModel、SQLAlchemy 以及 Python 与数据库交互的总体工作方式了解了很多。

如果你没有完全理解,也没关系,你总是可以稍后回来 refresh 这些概念。

我认为这可能是导致问题并让你挠头的最主要类型的错误之一。所以,干得好,好好学习了它!💪