使用实体关系

在 Pony 中,实体可以通过关系属性与其他实体关联。每个关系都有两个端点,由两个实体属性定义

class Person(db.Entity):
    cars = Set('Car')

class Car(db.Entity):
    owner = Optional(Person)

在上面的示例中,我们使用 Person.carsCar.owner 属性定义了 PersonCar 实体之间的一对多关系。

让我们为我们的实体添加几个数据属性

from pony.orm import *

db = Database()

class Person(db.Entity):
    name = Required(str)
    cars = Set('Car')

class Car(db.Entity):
    make = Required(str)
    model = Required(str)
    owner = Optional(Person)

db.bind('sqlite', ':memory:')
db.generate_mapping(create_tables=True)

现在让我们创建 PersonCar 实体的实例

>>> p1 = Person(name='John')
>>> c1 = Car(make='Toyota', model='Camry')
>>> commit()

通常,在您的程序中,您不需要手动调用函数 commit(),因为它应该由 db_session() 自动调用。但是,当您在交互模式下工作时,您永远不会离开 db_session(),这就是为什么如果我们想将数据存储到数据库中,我们需要手动提交的原因。

建立关系

在我们创建实例 p1c1 之后,它们之间没有建立关系。让我们检查关系属性的值

>>> print c1.owner
None

>>> print p1.cars
CarSet([])

属性 cars 具有一个空集。

现在让我们在这两个实例之间建立关系

>>> c1.owner = p1

如果我们现在打印关系属性的值,那么我们将看到以下内容

>>> print c1.owner
Person[1]

>>> print p1.cars
CarSet([Car[1]])

当我们为 Car 实例分配所有者时,Person.cars 关系属性立即反映了更改。

我们也可以在创建 Car 实例时分配关系属性来建立关系

>>> p1 = Person(name='John')
>>> c1 = Car(make='Toyota', model='Camry', owner=p1)

在我们的示例中,属性 owner 是可选的,因此我们可以随时为它分配一个值,无论是在创建 Car 实例时,还是在之后。

集合操作

属性 Person.cars 表示为一个集合,因此我们可以使用适用于集合的常规操作:add()remove()inlen()clear()

您可以使用 Set.add()Set.remove() 方法添加或删除关系

>>> p1.cars.remove(Car[1])
>>> print p1.cars
CarSet([])

>>> p1.cars.add(Car[1])
>>> print p1.cars
CarSet([Car[1]])

您可以检查集合是否包含元素

>>> Car[1] in p1.cars
True

或者确保集合中不存在此类元素

>>> Car[1] not in p1.cars
False

检查集合长度

>>> len(p1.cars)
1

如果您需要创建一个汽车实例并将其分配给特定的人员实例,则有几种方法可以做到。其中一种选择是调用集合属性的 create() 方法

>>> p1.cars.create(model='Toyota', make='Prius')
>>> commit()

现在我们可以检查是否将新的 Car 实例添加到我们实例的 Person.cars 集合属性中

>>> print p1.cars
CarSet([Car[2], Car[1]])
>>> p1.cars.count()
2

您可以遍历集合属性

>>> for car in p1.cars:
...     print car.model

Toyota
Camry

属性提升

在 Pony 中,集合属性提供属性提升功能:集合获取其项目的属性

>>> show(Car)
class Car(Entity):
    id = PrimaryKey(int, auto=True)
    make = Required(str)
    model = Required(str)
    owner = Optional(Person)
>>> p1 = Person[1]
>>> print p1.cars.model
Multiset({u'Camry': 1, u'Prius': 1})

这里我们使用 show() 函数打印出实体声明,然后打印 cars 关系属性的 model 属性的值。cars 属性具有 Car 实体的所有属性:idmakemodelowner。在 Pony 中,我们称之为 Multiset,它使用字典实现。字典的键表示属性的值 - 在我们的示例中为 'Camry' 和 'Prius'。字典的值显示它在此集合中出现的次数。

>>> print p1.cars.make
Multiset({u'Toyota': 2})

Person[1] 有两辆丰田。

我们可以遍历 multiset

>>> for m in p1.cars.make:
...     print m
...
Toyota
Toyota

集合属性参数

以下是您可以应用于集合属性的选项列表

示例

class Photo(db.Entity):
    tags = Set('Tag', table='photo_to_tag', column='tag_id')

class Tag(db.Entity):
    photos = Set(Photo)

集合属性查询和其他方法

从 0.6.1 版本开始,Pony 引入了关系属性的查询。

您可以使用关系属性的以下方法来检索数据

有关更多详细信息,请参阅 API 参考的 集合属性方法 部分。

下面您可以找到使用这些方法的几个示例。我们将使用 University 架构来展示这些查询,以下是 python 实体定义实体关系图

以下示例选择所有在 101 组中 gpa 大于 3 的学生

g = Group[101]
g.students.filter(lambda student: student.gpa > 3)[:]

此查询可用于显示按 name 属性排序的 101 组学生列表的第二页

g.students.order_by(Student.name).page(2, pagesize=3)

相同的查询也可以用以下形式编写

g.students.order_by(lambda s: s.name).limit(3, offset=3)

以下查询从 101 组中返回两个随机学生

g.students.random(2)

还有一个例子。此查询返回 Student[1] 在第二学期修过的课程的第一页,按课程名称排序

s = Student[1]
s.courses.select(lambda c: c.semester == 2).order_by(Course.name).page(1)