05.实体关系
实体之间可以相互关联,两个实体之间的关系是由两个属性来定义的,这两个属性指定了关系的两端:
class Customer(db.Entity):
orders = Set('Order')
class Order(db.Entity):
customer = Required(Customer)
在上面的例子中,我们有两个关系属性:orders
和customer
,当我们定义实体Customer
时,实体Order
还没有定义,这就是为什么我们要在订单属性中的订单周围加上引号。
另一种方法是使用lambda:
class Customer(db.Entity):
orders = Set(lambda: Order)
如果你想让你的IDE检查声明的实体名称并高亮显示错别字,这可能很有用。
一些映射器 (例如 Django) 只要求在一侧定义关系,Pony 要求在两边显式地定义关系 (正如《Python之禅》中所说:显式比隐式更好),这使得用户可以从每个实体的角度看所有的关系。
所有的关系都是双向的。如果你更新了一个关系的一边,另一边也会自动更新。例如,如果我们创建了一个订单实体Order
的实例,客户的订单集将被更新至包含这个新订单。
关系有三种类型:一对一、一对多和多对多关系。一对一的关系很少使用,大多数实体之间的关系是一对多和多对多的关系。
如果两个实体之间有一对一的关系,那往往意味着它们可以组合成一个实体。
如果你的数据图有很多一对一的关系,那么这可能是你需要重新考虑实体定义的信号。
个人意见:这个需要根据实际需求来定,如果一个表很宽(字段很多),这时候可以根据业务及使用频率,考虑将其拆分为多个表——gthank
一对多的关系
下面是一个单对多关系的例子:
class Order(db.Entity):
items = Set("OrderItem")
class OrderItem(db.Entity):
order = Required(Order)
在上面的例子中,没有订单,OrderItem的实例就不能存在。如果我们想让OrderItem的实例不被分配给一个订单而存在,我们可以将订单属性定义为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")
不允许将这两个属性定义为必填,因为这样做没有意义。
自我引用
一个实体可以使用自我参照关系与自身建立关系。这种关系可以分为两种类型:对称关系和非对称关系。
非对称关系是由属于同一实体的两个属性定义的。
对称关系的具体情况是,实体只指定了一个关系属性,而这个属性定义了关系的双方。这种关系可以是一对一的关系,也可以是多对多的关系。下面是自引用关系的例子。
class Person(db.Entity):
name = Required(str)
spouse = Optional("Person", reverse="spouse") # 对称的一对一
friends = Set("Person", reverse="friends") # 对称的多对多
manager = Optional("Person", reverse="employees") # 非对称的一面
employees = Set("Person", reverse="manager") # 非对称的另一面
两个实体之间的多重关系
当两个实体之间有一个以上的关系时,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")
在上面的例子中,我们必须指定反向选项。如果你试图为实体定义生成映射而不指定反向属性,你会得到异常pony.orm.core.erDiagramError
: "Ambiguous reverse attribute for Tweet.author"
。
之所以出现这种情况,是因为属性author
在技术上可以与tweets
或favorites
相关,而Pony没有任何信息说明应该使用哪个属性。
网友评论