【问题标题】:Pony ORM JOIN syntaxPony ORM JOIN 语法
【发布时间】:2017-06-10 09:58:59
【问题描述】:

我已经开始使用 Pony,但还没有真正理解如何使用连接。在我看到的示例中,left_join() 与两个 for 子句一起使用,但是当我尝试在我的代码中重复它时,我收到类似“collection is expected, got”for p in Pond”之类的错误 也许有人可以解释如何使用它或将我指向已经解释过的文档页面?

【问题讨论】:

    标签: ponyorm


    【解决方案1】:

    假设我们有以下实体:

    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
    

    如您所见,虽然 Arthur 的年龄超过 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()
    

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

    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 命令的 LEFT JOIN 子句的 ON 部分。

    如果使用left_join,则不能像第一个查询那样进行显式连接,因为在这种情况下,Pony 不知道将哪个条件放入LEFT JOIN 子句的ON 部分。

    有时手动指定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 到 Person 的属性遍历。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多