Pony ORM JOIN语法

时间:2017-01-25 09:30:26

标签: ponyorm

我已经开始使用Pony,但实际上还没有理解如何使用连接。在示例中,我看到当left_join()与两个for子句一起使用时,但当我尝试在我的代码中重复它时,我得到的错误就像“收集预期,得到”p中的p“ 也许有人可以解释如何使用它或指向我已经解释过的文档页面?

1 个答案:

答案 0 :(得分:7)

我们说我们有以下实体:

from pony import orm

db = orm.Database()

class Person(db.Entity):
    id = orm.PrimaryKey(int, auto=True)
    name = orm.Required(str)
    age = orm.Required(int)
    contacts = orm.Set('Contact')

class Contact(db.Entity):
    id = orm.PrimaryKey(int, auto=True)
    person = orm.Required('Person')
    type = orm.Required(str)
    value = orm.Required(str)

db.generate_mapping(create_tables=True)

with orm.db_session:
    john = Person(name='John', age=23)
    mary = Person(name='Mary', age=21)
    mike = Person(name='Mike', age=18)
    arthur = Person(name='Arthur', age=25)

    john.contacts.create(type='mobile', value='1234567')
    john.contacts.create(type='email', value='john@example.com')

    mary.contacts.create(type='mobile', value='76543321')
    mary.contacts.create(type='skype', value='mary123')

    mike.contacts.create(type='mobile', value='2345678')

现在我们要为20岁以上的每个人打印人名和联系信息。我们可以通过多种方式来实现目标。

第一种方式是我们明确说明连接条件。这种方式非常冗长:

query = orm.select(
    (p.name, c.value)
    for p in Person for c in Contact
    if p.age > 20 and c.person == p
)
query.show()

在此查询中,我们明确说明了连接条件:c.person == p。该查询将向我们显示以下结果:

p.name|c.type|c.value
------+------+----------------
John  |email |john@example.com
John  |mobile|1234567
Mary  |mobile|76543321
Mary  |skype |mary123

正如你所看到的,亚瑟没有被纳入结果,虽然他的年龄大于20.这是因为这种类型的连接是内连接,结果只包括可以找到至少一个的人接触。

第二种加入方式是我们循环收集属性:

query = orm.select(
    (p.name, c.value)
    for p in Person for c in p.contacts
    if p.age > 20
)
query.show()

这种类型的连接最常用。这非常方便,因为我们不需要明确指定连接条件。查询结果与之前相同:

p.name|c.type|c.value
------+------+----------------
John  |email |john@example.com
John  |mobile|1234567
Mary  |mobile|76543321
Mary  |skype |mary123
由于和以前一样的原因,亚瑟仍然不在名单中。如果我们想将Arthur包含在结果中,我们需要使用其他类型的连接,即左连接:

query = orm.left_join(
    (p.name, c.value)
    for p in Person for c in p.contacts
    if p.age > 20
)
query.show()

在这种情况下,查询结果包括Arthur的None值而不是电话号码:

p.name|c.type|c.value
------+------+----------------
Arthur|None  |None
John  |email |john@example.com
John  |mobile|1234567
Mary  |mobile|76543321
Mary  |skype |mary123

当您使用left_join时,您需要循环收集。在这种情况下,Pony将连接条件添加到SQL命令的ON子句的LEFT JOIN部分。

如果您使用left_join,则无法在第一个查询中执行显式连接,因为在这种情况下,Pony不知道将哪个条件放入ON子句的LEFT JOIN部分

有时,手动指定ON部分的内容可能很有用。目前Pony不支持此类查询,但此功能可能会在将来添加。

在许多情况下使用PonyORM时,可以在不进行连接的情况下检索数据。例如,您可以编写以下循环来打印人名和电话号码:

with db_session:
    for p in Person.select(lambda p: p.age > 20):
        print(p.name)
        for c in p.contacts:
            print(c.type, c.value)

在其他ORM中,这将导致" N + 1查询"问题,通过单独的SQL查询检索每个人的联系人。 Pony尝试自动优化查询以避免" N + 1查询"图案。

在某些情况下,连接是隐含的。例如,要查找姓名以' M'开头的人的所有联系人,您可以写:

query = select(c for c in Contact if c.person.name.startswith('M'))
for c in query:
    print(c.person.name, c.type, c.value)

在这种情况下,Person表是隐式连接的,只是因为您执行从Contact to Person遍历的属性。