实体关系¶
实体之间可以相互关联。两个实体之间的关系通过使用两个属性来定义,这两个属性指定了关系的两端。
class Customer(db.Entity):
orders = Set('Order')
class Order(db.Entity):
customer = Required(Customer)
在上面的示例中,我们有两个关系属性:orders
和 customer
。当我们定义实体 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
-Required
或 Optional
-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
在技术上可以与属性 tweets
或 favorites
相关联,而 Pony 没有关于使用哪一个的信息。