创建连接表¶
现在我们将处理放置在不同表中的连接数据。
因此,第一步是创建多个表并将它们连接起来,以便一个表中的每一行都可以引用另一个表中的另一行。
我们一直在单个表 hero
中处理英雄。现在让我们添加一个表 team
。
团队表将如下所示
id | 名称 | 总部 |
---|---|---|
1 | Preventers | Sharp Tower |
2 | Z-Force | Sister Margaret's Bar |
为了连接它们,我们将在 hero 表中添加另一列,以 team_id
通过 ID 指向每个团队
id | 名称 | secret_name | age | team_id ✨ |
---|---|---|---|---|
1 | Deadpond | Dive Wilson | null | 2 ✨ |
2 | Spider-Boy | Pedro Parqueador | null | 1 ✨ |
3 | Rusty-Man | Tommy Sharp | 48 | 1 ✨ |
这样,表 hero
中的每一行都可以指向表 team
中的一行
一对多和多对一¶
在这里,我们正在创建连接数据,其关系是一个团队可以拥有多个英雄。因此,它通常被称为一对多或多对一关系。
如果我们从英雄开始看,就可以看到多对一部分,多个英雄可以是一个团队的一部分。
这可能是最流行的关系类型,所以我们将从它开始。但也有多对多和一对一关系。
在代码中创建表¶
创建 team
表¶
让我们从在代码中创建表开始。
从 sqlmodel
导入我们需要的东西,并创建一个新的 Team
模型
from sqlmodel import Field, SQLModel, create_engine
class Team(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
# Code below omitted 👇
👀 完整文件预览
from sqlmodel import Field, SQLModel, create_engine
class Team(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: int | None = Field(default=None, index=True)
team_id: int | None = Field(default=None, foreign_key="team.id")
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 main():
create_db_and_tables()
if __name__ == "__main__":
main()
🤓 其他版本和变体
from typing import Optional
from sqlmodel import Field, SQLModel, create_engine
class Team(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: Optional[int] = Field(default=None, index=True)
team_id: Optional[int] = Field(default=None, foreign_key="team.id")
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 main():
create_db_and_tables()
if __name__ == "__main__":
main()
这与我们一直在 Hero
模型中做的事情非常相似。
Team
模型将自动在名为 "team"
的表中,并且它将具有以下列
id
,主键,由数据库自动生成name
,团队的名称- 我们还告诉 SQLModel 为此列创建一个索引
headquarters
,团队的总部
最后,我们在配置中将其标记为表。
创建新的 hero
表¶
现在让我们创建 hero
表。
这与我们目前一直在使用的模型相同,我们只是添加了新列 team_id
from sqlmodel import Field, SQLModel, create_engine
class Team(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: int | None = Field(default=None, index=True)
team_id: int | None = Field(default=None, foreign_key="team.id")
# Code below omitted 👇
👀 完整文件预览
from sqlmodel import Field, SQLModel, create_engine
class Team(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: int | None = Field(default=None, index=True)
team_id: int | None = Field(default=None, foreign_key="team.id")
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 main():
create_db_and_tables()
if __name__ == "__main__":
main()
🤓 其他版本和变体
from typing import Optional
from sqlmodel import Field, SQLModel, create_engine
class Team(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: Optional[int] = Field(default=None, index=True)
team_id: Optional[int] = Field(default=None, foreign_key="team.id")
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 main():
create_db_and_tables()
if __name__ == "__main__":
main()
大多数内容应该看起来很熟悉
该列将命名为 team_id
。它将是一个整数,并且在数据库中可以为 NULL
(或在 Python 中为 None
),因为可能有一些英雄不属于任何团队。
我们在 Field()
中添加了 None
的默认值,这样我们在创建英雄时就不必显式传递 team_id=None
。
现在,这是新部分
在 Field()
中,我们传递参数 foreign_key="team.id"
。这告诉数据库,此列 team_id
是表 team
的外键。“外键”只是意味着此列将具有键,以标识外部表中的行。
此列 team_id
中的值将与 team
表上 id
列中某行中的整数相同。这就是连接两个表的方式。
foreign_key
的值¶
请注意,foreign_key
是一个字符串。
在其中包含表的名称,然后是一个点,然后是列的名称。
这是数据库中表的名称,因此它是 "team"
,而不是模型类 Team
(带有大写 T
)的名称。
如果您有自定义表名,则将使用该自定义表名。
信息
您可以在高级用户指南中了解有关为模型设置自定义表名的信息。
创建表¶
现在我们可以添加与以前相同的代码来创建引擎和创建表的函数
# Code above omitted 👆
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)
# Code below omitted 👇
👀 完整文件预览
from sqlmodel import Field, SQLModel, create_engine
class Team(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: int | None = Field(default=None, index=True)
team_id: int | None = Field(default=None, foreign_key="team.id")
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 main():
create_db_and_tables()
if __name__ == "__main__":
main()
🤓 其他版本和变体
from typing import Optional
from sqlmodel import Field, SQLModel, create_engine
class Team(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: Optional[int] = Field(default=None, index=True)
team_id: Optional[int] = Field(default=None, foreign_key="team.id")
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 main():
create_db_and_tables()
if __name__ == "__main__":
main()
和以前一样,我们将从另一个函数 main()
调用此函数,并且我们将函数 main()
添加到文件的主块中
# Code above omitted 👆
def main():
create_db_and_tables()
if __name__ == "__main__":
main()
👀 完整文件预览
from sqlmodel import Field, SQLModel, create_engine
class Team(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: int | None = Field(default=None, index=True)
team_id: int | None = Field(default=None, foreign_key="team.id")
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 main():
create_db_and_tables()
if __name__ == "__main__":
main()
🤓 其他版本和变体
from typing import Optional
from sqlmodel import Field, SQLModel, create_engine
class Team(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: Optional[int] = Field(default=None, index=True)
team_id: Optional[int] = Field(default=None, foreign_key="team.id")
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 main():
create_db_and_tables()
if __name__ == "__main__":
main()
运行代码¶
提示
在运行代码之前,请确保删除文件 database.db
以确保从头开始。
如果我们运行到目前为止的代码,它将去创建数据库文件 database.db
和我们在其中定义的表,team
和 hero
$ python app.py
// Automatically start a new transaction
INFO Engine BEGIN (implicit)
// Check if the tables exist already
INFO Engine PRAGMA main.table_info("team")
INFO Engine [raw sql] ()
INFO Engine PRAGMA temp.table_info("team")
INFO Engine [raw sql] ()
INFO Engine PRAGMA main.table_info("hero")
INFO Engine [raw sql] ()
INFO Engine PRAGMA temp.table_info("hero")
INFO Engine [raw sql] ()
// Create the tables
INFO Engine
CREATE TABLE team (
id INTEGER,
name VARCHAR NOT NULL,
headquarters VARCHAR NOT NULL,
PRIMARY KEY (id)
)
INFO Engine [no key 0.00010s] ()
INFO Engine
CREATE TABLE hero (
id INTEGER,
name VARCHAR NOT NULL,
secret_name VARCHAR NOT NULL,
age INTEGER,
team_id INTEGER,
PRIMARY KEY (id),
FOREIGN KEY(team_id) REFERENCES team (id)
)
INFO Engine [no key 0.00026s] ()
INFO Engine COMMIT
在 SQL 中创建表¶
让我们看看相同的生成的 SQL 代码。
正如我们之前看到的,这些 VARCHAR
列在 SQLite 中转换为 TEXT
,SQLite 是我们用于这些实验的数据库。
因此,第一个 SQL 也可以写成
CREATE TABLE team (
id INTEGER,
name TEXT NOT NULL,
headquarters TEXT NOT NULL,
PRIMARY KEY (id)
)
第二个表也可以写成
CREATE TABLE hero (
id INTEGER,
name TEXT NOT NULL,
secret_name TEXT NOT NULL,
age INTEGER,
team_id INTEGER,
PRIMARY KEY (id),
FOREIGN KEY(team_id) REFERENCES team (id)
)
唯一的新内容是 FOREIGN KEY
行,如您所见,它告诉数据库此表中的哪一列是外键( team_id
),它引用了哪个其他(外部)表( team
),以及该表中的哪一列是用于定义要连接的行的键( id
)。
请随意在 DB Browser for SQLite 中进行实验。
回顾¶
使用 SQLModel ,在大多数情况下,您只需要在 Field()
中使用带有 foreign_key
的字段(列),并使用指向另一个表和列的字符串来连接两个表。
现在我们已经创建并连接了表,让我们在下一章中创建一些行。🚀