跳到内容

创建行 - 使用会话 - INSERT

现在我们有了数据库和表,我们可以开始添加数据了。

这里是表的样式提醒,这是我们想要添加的数据

idnamesecret_nameage
1死侍戴夫·威尔逊
2蜘蛛男孩佩德罗·帕尔克多
3锈人汤米·夏普48

创建表和数据库

我们将从上一章中断的地方继续。

这是我们创建数据库和表的代码,这里没有什么新内容

from sqlmodel import Field, SQLModel, create_engine  # (2)!


class Hero(SQLModel, table=True):  # (3)!
    id: int | None = Field(default=None, primary_key=True)  # (4)!
    name: str  # (5)!
    secret_name: str  # (6)!
    age: int | None = None  # (7)!


sqlite_file_name = "database.db"  # (8)!
sqlite_url = f"sqlite:///{sqlite_file_name}"  # (9)!

engine = create_engine(sqlite_url, echo=True)  # (10)!


def create_db_and_tables():  # (11)!
    SQLModel.metadata.create_all(engine)  # (12)!

# More code here later 👈

if __name__ == "__main__":  # (13)!
    create_db_and_tables()  # (14)!
  1. typing 导入 Optional 以声明可能为 None 的字段。
  2. sqlmodel 导入我们需要的项:FieldSQLModelcreate_engine
  3. 创建 Hero 模型类,表示数据库中的 hero 表。

    并使用 table=True 将此类标记为表模型

  4. 创建 id 字段

    在数据库为其赋值之前,它可能为 None,因此我们用 Optional 注释它。

    它是一个主键,因此我们使用 Field() 和参数 primary_key=True

  5. 创建 name 字段。

    它是必需的,所以没有默认值,也不是 Optional

  6. 创建 secret_name 字段。

    也是必需的。

  7. 创建 age 字段。

    它不是必需的,默认值为 None

    在数据库中,默认值为 NULL,即 SQL 中 None 的等效值。

    由于此字段可能为 None(在数据库中为 NULL),我们用 Optional 注释它。

  8. 写入数据库文件的名称。

  9. 使用数据库文件的名称创建数据库 URL。
  10. 使用 URL 创建引擎。

    此时尚未创建数据库,没有文件或表被创建,只有引擎对象将处理与此特定数据库的连接,并专门支持 SQLite(基于 URL)。

  11. 将产生副作用的代码放在函数中。

    在这种情况下,只有一行代码会创建带表的数据库文件。

  12. 创建在 SQLModel.metadata 中自动注册的所有表。

  13. 添加一个主块,或“顶级脚本环境”。

    并放入一些逻辑,以便在直接使用 Python 调用时执行,例如

    $ python app.py
    
    // Execute all the stuff and show the output
    

    ...但当从该模块导入某些内容时不会执行,例如

    from app import Hero
    
  14. 在此主块中,调用创建数据库文件和表的函数。

    这样,当我们用以下方式调用它时

    $ python app.py
    
    // Doing stuff ✨
    

    ...它将创建数据库文件和表。

from typing import Optional  # (1)!

from sqlmodel import Field, SQLModel, create_engine  # (2)!


class Hero(SQLModel, table=True):  # (3)!
    id: Optional[int] = Field(default=None, primary_key=True)  # (4)!
    name: str  # (5)!
    secret_name: str  # (6)!
    age: Optional[int] = None  # (7)!


sqlite_file_name = "database.db"  # (8)!
sqlite_url = f"sqlite:///{sqlite_file_name}"  # (9)!

engine = create_engine(sqlite_url, echo=True)  # (10)!


def create_db_and_tables():  # (11)!
    SQLModel.metadata.create_all(engine)  # (12)!

# More code here later 👈

if __name__ == "__main__":  # (13)!
    create_db_and_tables()  # (14)!
  1. typing 导入 Optional 以声明可能为 None 的字段。
  2. sqlmodel 导入我们需要的项:FieldSQLModelcreate_engine
  3. 创建 Hero 模型类,表示数据库中的 hero 表。

    并使用 table=True 将此类标记为表模型

  4. 创建 id 字段

    在数据库为其赋值之前,它可能为 None,因此我们用 Optional 注释它。

    它是一个主键,因此我们使用 Field() 和参数 primary_key=True

  5. 创建 name 字段。

    它是必需的,所以没有默认值,也不是 Optional

  6. 创建 secret_name 字段。

    也是必需的。

  7. 创建 age 字段。

    它不是必需的,默认值为 None

    在数据库中,默认值为 NULL,即 SQL 中 None 的等效值。

    由于此字段可能为 None(在数据库中为 NULL),我们用 Optional 注释它。

  8. 写入数据库文件的名称。

  9. 使用数据库文件的名称创建数据库 URL。
  10. 使用 URL 创建引擎。

    此时尚未创建数据库,没有文件或表被创建,只有引擎对象将处理与此特定数据库的连接,并专门支持 SQLite(基于 URL)。

  11. 将产生副作用的代码放在函数中。

    在这种情况下,只有一行代码会创建带表的数据库文件。

  12. 创建在 SQLModel.metadata 中自动注册的所有表。

  13. 添加一个主块,或“顶级脚本环境”。

    并放入一些逻辑,以便在直接使用 Python 调用时执行,例如

    $ python app.py
    
    // Execute all the stuff and show the output
    

    ...但当从该模块导入某些内容时不会执行,例如

    from app import Hero
    
  14. 在此主块中,调用创建数据库文件和表的函数。

    这样,当我们用以下方式调用它时

    $ python app.py
    
    // Doing stuff ✨
    

    ...它将创建数据库文件和表。

现在我们能够创建数据库和表了,我们将从这一点继续,在同一个文件中添加更多代码来创建数据。

用 SQL 创建数据

在处理 Python 代码之前,让我们看看如何用 SQL 创建数据。

假设我们想将 Deadpond 的记录/行插入到我们的数据库中。

我们可以使用以下 SQL 代码来完成此操作

INSERT INTO "hero" ("name", "secret_name")
VALUES ("Deadpond", "Dive Wilson");

它的意思大致是

嘿 SQL 数据库👋,请在 "hero" 表中 INSERT 一些东西(创建一条记录/行)。

我希望您在这些特定列中插入一行带有某些值

  • "name"
  • "secret_name"

我希望您放入这些列的值是

  • "Deadpond"
  • "Dive Wilson"

在 SQLite 的 DB 浏览器中尝试

您可以在 DB Explorer for SQLite 中尝试该 SQL 语句。

确保通过单击 打开数据库 并选择相同的 database.db 文件来打开我们已经创建的数据库。

提示

如果您没有带有 hero 表的 database.db 文件,您可以通过运行顶部的 Python 程序重新创建它。👆

然后转到 执行 SQL 选项卡并复制上面的 SQL。

它看起来像这样

点击“执行全部”按钮。

然后您可以转到 浏览数据 选项卡,您将看到您新创建的记录/行

数据库中的数据和代码中的数据

在使用编程语言处理数据库(SQL 或任何其他类型)时,我们总会有一些数据在内存中,存储在我们代码中创建的对象和变量中,并且会有一些数据在数据库中

我们不断地从数据库中获取一些数据并将其放入内存中的变量中。

同样,我们不断地在代码中创建带有数据的变量和对象,然后我们希望将其保存到数据库中,因此我们以某种方式发送它。

在某些情况下,我们甚至可以在内存中创建一些数据,然后在保存到数据库之前对其进行更改和更新。

我们甚至可能会根据代码中的一些逻辑决定不再将数据保存到数据库中,然后直接将其删除。🔥 而且我们只在内存中处理了这些数据,而没有将其来回发送到数据库。

SQLModel 尽其所能(实际上是通过 SQLAlchemy)使这种交互尽可能简单、直观、熟悉或“接近编程”。✨

但是,在任何给定时刻,数据可能存在的两个位置(内存中或数据库中)的这种划分总是存在的。您需要牢记这一点。🤓

用 Python 和 SQLModel 创建数据

现在让我们用 Python 创建同一行。

首先,删除文件 database.db,以便我们可以从头开始。

因为我们有 Python 代码在内存中执行数据,而数据库是一个独立的系统(外部 SQLite 文件或外部数据库服务器),所以我们需要执行两个步骤

  • 在 Python 中,在内存中(在变量中)创建数据
  • 保存/发送数据到数据库

创建模型实例

让我们从第一步开始,在内存中创建数据。

我们已经创建了一个表示数据库中 hero 表的 Hero 类。

我们创建的每个实例都将表示数据库中一行数据。

所以,第一步是简单地创建 Hero 的一个实例。

我们将立即创建 3 个,用于 3 位英雄

# 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)

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

        session.commit()


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)

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

        session.commit()


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()

提示

此文件中的上述代码(省略的代码)与本章顶部所示的代码相同。

我们之前用于创建 Hero 模型的相同代码。

我们将其放入一个函数 create_heroes() 中,以便稍后完成时调用它。

如果您正在交互式地尝试代码,您也可以直接编写它。

创建会话

到目前为止,我们只使用了引擎与数据库进行交互。

引擎是我们与所有代码共享的单一对象,它负责与数据库通信,处理连接(在使用 PostgreSQL 或 MySQL 等服务器数据库时),等等。

但在使用 SQLModel 时,您将主要使用另一个位于其上的工具,即会话

与整个应用程序只有一个引擎不同,我们为每个相互关联的数据库操作组创建一个新的会话

事实上,会话需要并使用一个引擎

例如,如果我们有一个 Web 应用程序,我们通常会为每个请求设置一个单一的会话

我们将在所有代码中,在应用程序的任何地方(由所有请求共享)重复使用同一个引擎。但对于每个请求,我们将创建并使用一个新的会话。一旦请求完成,我们将关闭会话。

第一步是导入 Session

from sqlmodel import Field, Session, SQLModel, create_engine

# 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)

    session = Session(engine)

    session.add(hero_1)
    session.add(hero_2)
    session.add(hero_3)

    session.commit()

    session.close()


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)

    session = Session(engine)

    session.add(hero_1)
    session.add(hero_2)
    session.add(hero_3)

    session.commit()

    session.close()


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()

然后我们可以创建一个新的会话

# 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)

    session = Session(engine)

# 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)

    session = Session(engine)

    session.add(hero_1)
    session.add(hero_2)
    session.add(hero_3)

    session.commit()

    session.close()


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)

    session = Session(engine)

    session.add(hero_1)
    session.add(hero_2)
    session.add(hero_3)

    session.commit()

    session.close()


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()

新的 Sessionengine 作为参数。它将在底层使用引擎

提示

稍后我们将看到一种使用 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)

    session = Session(engine)

    session.add(hero_1)
    session.add(hero_2)
    session.add(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)

    session = Session(engine)

    session.add(hero_1)
    session.add(hero_2)
    session.add(hero_3)

    session.commit()

    session.close()


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)

    session = Session(engine)

    session.add(hero_1)
    session.add(hero_2)
    session.add(hero_3)

    session.commit()

    session.close()


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()

到目前为止,我们的英雄尚未存储在数据库中。

这就是会话独立于引擎有意义的情况之一。

会话将所有应稍后保存到数据库的对象保存在内存中。

一旦我们准备好,我们就可以提交这些更改,然后会话将使用底层的引擎通过向数据库发送适当的 SQL 来保存所有数据,从而创建所有行。所有操作都在一个批次中完成。

这使得与数据库的交互更高效(还有一些额外的好处)。

技术细节

会话将创建新事务并在此事务中执行所有 SQL 代码。

这确保了数据以单个批次保存,并且要么全部成功,要么全部失败,但不会使数据库处于损坏状态。

提交会话更改

现在我们已经在会话中拥有了英雄,并且我们准备好将所有这些保存到数据库中,我们可以提交更改

# 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)

    session = Session(engine)

    session.add(hero_1)
    session.add(hero_2)
    session.add(hero_3)

    session.commit()

# 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)

    session = Session(engine)

    session.add(hero_1)
    session.add(hero_2)
    session.add(hero_3)

    session.commit()

    session.close()


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)

    session = Session(engine)

    session.add(hero_1)
    session.add(hero_2)
    session.add(hero_3)

    session.commit()

    session.close()


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()

一旦执行此行,会话将使用引擎通过发送相应的 SQL 来保存数据库中的所有数据。

将英雄创建为脚本

创建英雄的函数现在准备就绪。

现在我们只需要确保当我们直接使用 Python 运行此程序时调用它。

我们已经有一个主块,像这样

if __name__ == "__main__":
    create_db_and_tables()

我们可以将新函数添加到那里,像这样

if __name__ == "__main__":
    create_db_and_tables()
    create_heroes()

但为了使事情更有条理,我们不妨创建一个新的函数 main(),它将包含作为独立脚本调用时应执行的所有代码,我们可以在其中放入之前的函数 create_db_and_tables(),并添加新的函数 create_heroes()

# Code above omitted 👆

def main():
    create_db_and_tables()
    create_heroes()

# 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)

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

        session.commit()


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)

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

        session.commit()


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()

然后我们可以从主块中调用那个单一的 main() 函数

# Code above omitted 👆

def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()
👀 完整文件预览
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)

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

        session.commit()


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)

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

        session.commit()


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()

通过将所有应作为脚本执行的代码放在一个函数中,我们可以轻松地在以后添加更多代码。

如果需要,其他代码也可以导入和使用相同的 main() 函数。

运行脚本

现在我们可以从控制台将程序作为脚本运行。

因为我们使用 echo=True 创建了引擎,它将打印出所有正在执行的 SQL 代码

$ python app.py
// Some boilerplate, checking that the hero table already exists
INFO Engine BEGIN (implicit)
INFO Engine PRAGMA main.table_info("hero")
INFO Engine [raw sql] ()
INFO Engine COMMIT
// BEGIN a transaction automatically ✨
INFO Engine BEGIN (implicit)
// Our INSERT statement, it uses VALUES (?, ?, ?) as parameters
INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
// ...and these are the parameter values 🚀
INFO Engine [generated in 0.00013s] ('Deadpond', 'Dive Wilson', None)
// Again, for Spider-Boy
INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
INFO Engine [cached since 0.000755s ago] ('Spider-Boy', 'Pedro Parqueador', None)
// And now for Rusty-Man
INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
INFO Engine [cached since 0.001014s ago] ('Rusty-Man', 'Tommy Sharp', 48)
// All good? Yes, commit this transaction! 🎉
INFO Engine COMMIT

如果你用过 Git,这工作方式非常相似。

我们使用 session.add() 将新对象(模型实例)添加到会话中(类似于 git add)。

这最终会形成一组准备好保存但尚未保存的数据。

我们可以进行更多修改,添加更多对象等。

一旦我们准备好,我们就可以在一个步骤中提交所有更改(类似于 git commit)。

关闭会话

会话持有某些资源,例如引擎的连接。

所以一旦我们完成了会话,我们应该关闭它以使其释放这些资源并完成清理

# 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)

    session = Session(engine)

    session.add(hero_1)
    session.add(hero_2)
    session.add(hero_3)

    session.commit()

    session.close()

# 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)

    session = Session(engine)

    session.add(hero_1)
    session.add(hero_2)
    session.add(hero_3)

    session.commit()

    session.close()


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)

    session = Session(engine)

    session.add(hero_1)
    session.add(hero_2)
    session.add(hero_3)

    session.commit()

    session.close()


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()

但是如果我们忘记关闭会话会发生什么?

或者如果代码中发生异常,并且它永远无法到达 session.close() 呢?

为此,有一种更好的方式来创建和关闭会话,使用 with 块。👇

with 块中的会话

了解 Session 的工作原理以及如何手动创建和关闭它是有益的。例如,如果您想在交互式会话中(例如使用 Jupyter)探索代码,它可能会很有用。

但是,有一种更好的方法来处理会话,那就是使用 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)

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

        session.commit()

# 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)

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

        session.commit()


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)

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

        session.commit()


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()

这与手动创建会话然后手动关闭它相同。但是这里,使用 with 块,它将在 with开始时自动创建并分配给变量 session,并在 with结束后自动关闭。

即使代码中出现异常,它也能正常工作。😎

回顾所有代码

让我们最后看一眼整个文件。🔍

您已经了解了创建 Hero 模型类、引擎以及创建数据库和表的第一部分。

让我们关注新代码

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():  # (1)!
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")  # (2)!
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    with Session(engine) as session:  # (3)!
        session.add(hero_1)  # (4)!
        session.add(hero_2)
        session.add(hero_3)

        session.commit()  # (5)!
    # (6)!


def main():  # (7)!
    create_db_and_tables()  # (8)!
    create_heroes()  # (9)!


if __name__ == "__main__":  # (10)!
    main()  # (11)!
  1. 我们使用函数 create_heroes() 将此逻辑组合在一起。

  2. 创建 Hero 模型的每个对象/实例。

    它们中的每一个都代表一行数据。

  3. 使用 with 块,通过 engine 创建一个 Session

    新的会话将被分配给变量 session

    并且当 with 块完成时,它将自动关闭。

  4. 将每个对象/实例添加到会话中。

    这些对象中的每一个都代表数据库中的一行。

    它们都在会话中等待保存。

  5. 将更改提交到数据库。

    这将实际将数据发送到数据库。

    它将自动启动一个事务并以单个批次保存所有数据。

  6. 此时,在 with 块完成后,会话会自动关闭。

  7. 我们有一个 main() 函数,其中包含当程序作为从控制台运行的脚本时应执行的所有代码。

    这样我们以后就可以向此函数添加更多代码。

    然后我们将这个 main() 函数放在下面的主块中。

    由于它是一个单一函数,其他 Python 文件可以导入它并直接调用它。

  8. 在这个 main() 函数中,我们还在创建数据库和表。

    在以前的版本中,这个函数是在主块中直接调用的。

    但现在它只是在 main() 函数中被调用。

  9. 现在我们还在这个 main() 函数中创建英雄。

  10. 我们仍然有一个主块来执行一些代码,当程序作为脚本从命令行运行时,比如

    $ python app.py
    
    // Do whatever is in the main block 🚀
    
  11. 现在有一个单一的 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():  # (1)!
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")  # (2)!
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    with Session(engine) as session:  # (3)!
        session.add(hero_1)  # (4)!
        session.add(hero_2)
        session.add(hero_3)

        session.commit()  # (5)!
    # (6)!


def main():  # (7)!
    create_db_and_tables()  # (8)!
    create_heroes()  # (9)!


if __name__ == "__main__":  # (10)!
    main()  # (11)!
  1. 我们使用函数 create_heroes() 将此逻辑组合在一起。

  2. 创建 Hero 模型的每个对象/实例。

    它们中的每一个都代表一行数据。

  3. 使用 with 块,通过 engine 创建一个 Session

    新的会话将被分配给变量 session

    并且当 with 块完成时,它将自动关闭。

  4. 将每个对象/实例添加到会话中。

    这些对象中的每一个都代表数据库中的一行。

    它们都在会话中等待保存。

  5. 将更改提交到数据库。

    这将实际将数据发送到数据库。

    它将自动启动一个事务并以单个批次保存所有数据。

  6. 此时,在 with 块完成后,会话会自动关闭。

  7. 我们有一个 main() 函数,其中包含当程序作为从控制台运行的脚本时应执行的所有代码。

    这样我们以后就可以向此函数添加更多代码。

    然后我们将这个 main() 函数放在下面的主块中。

    由于它是一个单一函数,其他 Python 文件可以导入它并直接调用它。

  8. 在这个 main() 函数中,我们还在创建数据库和表。

    在以前的版本中,这个函数是在主块中直接调用的。

    但现在它只是在 main() 函数中被调用。

  9. 现在我们还在这个 main() 函数中创建英雄。

  10. 我们仍然有一个主块来执行一些代码,当程序作为脚本从命令行运行时,比如

    $ python app.py
    
    // Do whatever is in the main block 🚀
    
  11. 现在有一个单一的 main() 函数,其中包含在从控制台运行程序时应执行的所有代码。

    所以,这就是我们需要在主块中包含的所有内容。只需调用 main() 函数。

提示

通过单击代码中的每个数字气泡来回顾每行代码的作用。👆

您现在可以将其放入 app.py 文件中并用 Python 运行它。您将看到如上所示的输出。

之后,如果您使用 DB Browser for SQLite 打开数据库,您将在 浏览数据 选项卡中看到刚刚创建的数据

接下来是什么

现在您知道如何向数据库添加行了。🎉

现在是更好地理解为什么 id 字段在数据库中不能为 NULL 因为它是主键,但实际上在 Python 代码中可以为 None 的好时机。

我将在下一章告诉你。🚀