实体关系

实体之间可以相互关联。两个实体之间的关系通过使用两个属性来定义,这两个属性指定了关系的两端。

class Customer(db.Entity):
    orders = Set('Order')

class Order(db.Entity):
    customer = Required(Customer)

在上面的示例中,我们有两个关系属性:orderscustomer。当我们定义实体 Customer 时,实体 Order 尚未定义。这就是为什么我们在 orders 属性中需要用引号括住 Order。另一个选择是使用 lambda

class Customer(db.Entity):
    orders = Set(lambda: Order)

这在您希望 IDE 检查已声明实体的名称并突出显示拼写错误时很有用。

一些映射器(例如 Django)仅要求在一侧定义关系。Pony 要求在两侧明确定义关系(正如 Python 之禅所述:显式优于隐式),这允许用户从每个实体的角度查看所有关系。

所有关系都是双向的。如果您更新关系的一侧,另一侧将自动更新。例如,如果我们创建一个 Order 实体的实例,客户的订单集将更新以包含此新订单。

关系有三种类型:一对一、一对多和多对多。一对一关系很少使用,实体之间的大多数关系都是一对多和多对多。如果两个实体之间存在一对一关系,通常意味着它们可以合并成一个实体。如果您的数据图有很多一对一关系,那么它可能表明您需要重新考虑实体定义。

一对多关系

以下是一对多关系的示例

class Order(db.Entity):
    items = Set("OrderItem")

class OrderItem(db.Entity):
    order = Required(Order)

在上面的示例中,OrderItem 的实例不能在没有订单的情况下存在。如果我们希望允许 OrderItem 的实例在未分配给订单的情况下存在,我们可以将 order 属性定义为 Optional

class Order(db.Entity):
    items = Set("OrderItem")

class OrderItem(db.Entity):
    order = Optional(Order)

多对多关系

为了创建多对多关系,您需要将关系的两端定义为 Set 属性

class Product(db.Entity):
    tags = Set("Tag")

class Tag(db.Entity):
    products = Set(Product)

为了在数据库中实现这种关系,Pony 将创建一个中间表。这是一个众所周知的解决方案,它允许您在关系数据库中拥有多对多关系。

一对一关系

为了创建一对一关系,关系属性应定义为 Optional-RequiredOptional-Optional

class Person(db.Entity):
    passport = Optional("Passport")

class Passport(db.Entity):
    person = Required("Person")

不允许将两个属性都定义为 Required,因为这没有意义。

自引用

实体可以使用自引用关系来关联自身。这种关系可以分为两种类型:对称和非对称。非对称关系由属于同一实体的两个属性定义。

对称关系的具体之处在于实体只有一个关系属性,并且该属性定义了关系的两端。这种关系可以是一对一或多对多。以下是自引用关系的示例

class Person(db.Entity):
    name = Required(str)
    spouse = Optional("Person", reverse="spouse") # symmetric one-to-one
    friends = Set("Person", reverse="friends")    # symmetric many-to-many
    manager = Optional("Person", reverse="employees") # one side of non-symmetric
    employees = Set("Person", reverse="manager") # another side of non-symmetric

两个实体之间的多重关系

当两个实体之间存在多个关系时,Pony 要求指定反向属性。这是为了让 Pony 知道哪对属性相互关联。让我们考虑一个数据图,其中用户可以写推文,也可以收藏推文

class User(db.Entity):
    tweets = Set("Tweet", reverse="author")
    favorites = Set("Tweet", reverse="favorited")

class Tweet(db.Entity):
    author = Required(User, reverse="tweets")
    favorited = Set(User, reverse="favorites")

在上面的示例中,我们必须指定选项 reverse。如果您尝试在没有指定 reverse 的情况下为实体定义生成映射,您将收到异常 pony.orm.core.ERDiagramError"Ambiguous reverse attribute for Tweet.author"。这是因为在这种情况下,属性 author 在技术上可以与属性 tweetsfavorites 相关联,而 Pony 没有关于使用哪一个的信息。