跳到内容

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

我希望你插入一行,在这些特定列中包含一些值

  • "name"
  • "secret_name"

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

  • "Deadpond"
  • "Dive Wilson"

在 SQLite 数据库浏览器中尝试

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

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

提示

如果你没有那个带有表 herodatabase.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() 中,以便稍后完成它后调用它。

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

创建一个 Session

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

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

但当使用 SQLModel 时,你将主要使用另一个在其之上的工具,即 Session

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

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

例如,如果有一个 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()

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

提示

稍后我们将看到一种使用 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 创建了 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() 将新对象(模型实例)添加到会话中(类似于 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 块创建使用 engineSession

    新的会话将被赋值给变量 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 块创建使用 engineSession

    新的会话将被赋值给变量 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的好时机。

我将在下一章告诉你这些。🚀