跳到内容

创建行 - 使用 Session - INSERT

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

这是一个关于表外观的提醒,这是我们要添加的数据

idnamesecret_nameage
1DeadpondDive Wilsonnull
2Spider-BoyPedro Parqueadornull
3Rusty-ManTommy Sharp48

创建表和数据库

我们将从上一章结束的地方继续。

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

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 数据库 👋,请 INSERT 一些东西(创建一个记录/行)INTO"hero"

我想让你插入一行,其中在这些特定列中包含一些值

  • "name"
  • "secret_name"

而我希望你放入这些列的值是

  • "Deadpond"
  • "Dive Wilson"

在 SQLite 的 DB Explorer 中尝试

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

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

提示

如果你没有包含表 herodatabase.db 文件,你可以通过运行顶部的 Python 程序来重新创建它。👆

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

它看起来像这样

单击“Execute all”按钮。

然后你可以转到 Browse Data 选项卡,你将看到你新创建的记录/行

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

当在编程语言中使用数据库(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() 中,以便在我们完成它后调用它。

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

创建 Session

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

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

但是,当使用 SQLModel 时,你将主要使用位于顶部的另一个工具,即 Session

engine 在整个应用程序中只有一个不同,我们为每个属于同一组的数据库操作创建一个新的 session

实际上,session 需要并使用 engine

例如,如果我们有一个 Web 应用程序,我们通常每个请求都有一个 session

我们将在应用程序中的所有代码中重用相同的 engine(由所有请求共享)。但是对于每个请求,我们将创建并使用一个新的 session。一旦请求完成,我们将关闭 session。

第一步是导入 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()

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

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

新的 Session 接受一个 engine 作为参数。它将在底层使用 engine

提示

稍后我们将看到使用 with 代码块创建 session 的更好方法。

将模型实例添加到 Session

现在我们有一些英雄模型实例(内存中的一些对象)和一个 session,下一步是将它们添加到 session 中

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

到目前为止,我们的英雄还 没有 存储在数据库中。

这是拥有独立于 enginesession 的意义之一。

session 在内存中保存所有应稍后保存在数据库中的对象。

一旦我们准备好,我们可以 提交 这些更改,然后 session 将使用底层的 engine 通过向数据库发送适当的 SQL 来保存所有数据,这样它将创建所有行。所有这些都在一个批处理中完成。

这使得与数据库的交互更加高效(加上一些额外的优势)。

技术细节

session 将创建一个新的事务,并在该事务中执行所有 SQL 代码。

这确保了数据以单个批处理形式保存,并且所有操作都将成功或全部失败,但不会使数据库处于损坏状态。

提交 Session 更改

现在我们已经在 session 中有了英雄,并且我们已准备好将所有内容保存到数据库中,我们可以 提交 更改

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

一旦执行此行,session 将使用 engine 通过发送相应的 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 创建了 engine,它将打印出它正在执行的所有 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() 将新对象(模型实例)添加到 session(类似于 git add)。

这最终会得到一组准备保存但尚未保存的数据。

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

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

关闭 Session

session 保留一些资源,例如来自 engine 的连接。

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

# 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 会发生什么?

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

为此,有一种更好的方法可以使用 with 代码块来创建和关闭 session。👇

with 代码块中的 Session

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

但是,有一种更好的方法可以使用 with 代码块来处理 session

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

这与手动创建 session 然后手动关闭它相同。但是在这里,使用 with 代码块,它将在 开始 with 代码块时自动创建并分配给变量 session,并且将在 with 代码块 完成 后自动关闭。

即使代码中存在异常,它也可以工作。😎

回顾所有代码

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

你已经知道创建 Hero 模型类、engine 以及创建数据库和表的第一部分。

让我们关注新代码

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 代码块创建使用 engineSession

    新的 session 将分配给变量 session

    并且将在 with 代码块完成时自动关闭。

  4. 将每个对象/实例添加到 session

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

    它们都等待在 session 中被保存。

  5. 提交 对数据库的更改。

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

    它将自动启动事务,并将所有数据保存在单个批处理中。

  6. 到目前为止,在 with 代码块完成后,session 将自动关闭。

  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 代码块创建使用 engineSession

    新的 session 将分配给变量 session

    并且将在 with 代码块完成时自动关闭。

  4. 将每个对象/实例添加到 session

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

    它们都等待在 session 中被保存。

  5. 提交 对数据库的更改。

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

    它将自动启动事务,并将所有数据保存在单个批处理中。

  6. 到目前为止,在 with 代码块完成后,session 将自动关闭。

  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 打开数据库,你将在 Browse Data 选项卡中看到你刚刚创建的数据

下一步

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

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

我将在下一章中告诉你有关内容。🚀