SQLAlchemy ORM - 建立关系

本课程介绍如何创建另一个表,该表与我们数据库中现有的表相关。客户表包含客户的主数据。我们现在需要创建invoices表,该表可能包含属于客户的任意数量的invoices。这是一对多关系的情况。

使用声明式,我们定义此表及其映射类invoices,如下所示 −

from sqlalchemy import create_engine, ForeignKey, Column, Integer, String
engine = create_engine('sqlite:///sales.db', echo = True)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
from sqlalchemy.orm import relationship

class Customer(Base):
   __tablename__ = 'customers'

   id = Column(Integer, primary_key = True)
   name = Column(String)
   address = Column(String)
   email = Column(String)

class Invoice(Base):
   __tablename__ = 'invoices'
   
   id = Column(Integer, primary_key = True)
   custid = Column(Integer, ForeignKey('customers.id'))
   invno = Column(Integer)
   amount = Column(Integer)
   customer = relationship("Customer", back_populates = "invoices")

Customer.invoices = relationship("Invoice", order_by = Invoice.id, back_populates = "customer")
Base.metadata.create_all(engine)

这将向 SQLite 引擎发送一个 CREATE TABLE 查询,如下所示 −

CREATE TABLE invoices (
   id INTEGER NOT NULL,
   custid INTEGER,
   invno INTEGER,
   amount INTEGER,
   PRIMARY KEY (id),
   FOREIGN KEY(custid) REFERENCES customers (id)
)

借助 SQLiteStudio 工具,我们可以检查 sales.db 中是否创建了新表。

Sales.db New Table

Invoices 类将 ForeignKey 构造应用于 custid 属性。此指令表示此列中的值应限制为 customers 表中 id 列中的值。这是关系数据库的核心功能,也是将不相连的表集合转换为具有丰富重叠关系的"粘合剂"。

第二个指令,称为 relationship(),告诉 ORM,应使用属性 Invoice.customer 将 Invoice 类链接到 Customer 类。relationship() 使用两个表之间的外键关系来确定此链接的性质,确定它是多对一的。

在 Customer.invoices 属性下的 Customer 映射类上放置了一个额外的 relationship() 指令。参数 relationship.back_populates 被指定为引用互补的属性名称,以便每个 relationship() 都可以对反向表达的相同关系做出智能决策。一方面,Invoices.customer 引用 Invoices 实例,另一方面,Customer.invoices 引用 Customers 实例列表。

relationship 函数是 SQLAlchemy ORM 包的 Relationship API 的一部分。它提供两个映射类之间的关系。这对应于父子或关联表关系。

以下是找到的基本关系模式 −

一对多

一对多关系在子表的外键的帮助下引用父级。然后在父级上指定 relationship(),作为引用子级所代表的项目集合。 relationship.back_populates 参数用于建立一对多的双向关系,其中"反向"一侧是多对一。

多对一

另一方面,多对一关系在父表中放置一个外键来引用子表。relationship() 在父表中声明,其中将创建一个新的标量持有属性。这里再次使用 relationship.back_populates 参数进行双向行为。

一对一

一对一关系本质上是一种双向关系。uselist 标志表示在关系的"多"侧放置标量属性而不是集合。要将一对多转换为一对一类型的关系,请将 uselist 参数设置为 false。

多对多

通过添加与两个类相关的关联表(使用它们的外键定义属性)来建立多对多关系。它由 relationship() 的次要参数指示。通常,Table 使用与声明性基类关联的 MetaData 对象,以便 ForeignKey 指令可以找到要链接的远程表。每个 relationship() 的 relationship.back_populates 参数建立双向关系。关系的两侧都包含一个集合。