API 参考

数据库

Database 类

class Database

The Database 对象使用连接池管理数据库连接。它是线程安全的,可以在应用程序的所有线程之间共享。The Database 对象允许使用 SQL 直接与数据库交互,但大多数情况下,您将使用实体并让 Pony 生成 SQL 语句来对数据库进行相应的更改。您可以同时使用多个数据库,为每个数据库使用一个单独的 Database 对象,但每个实体始终属于一个数据库。

bind(provider, *args, **kwargs)
bind(*args, **kwargs)

将实体绑定到数据库。

参数
  • provider (str) – 数据库提供程序的名称。数据库提供程序是一个位于 pony.orm.dbproviders 包中的模块。它知道如何与特定数据库交互。在数据库提供程序名称之后,您应该指定将传递给相应 DBAPI 驱动程序的 connect() 方法的参数。Pony 附带以下提供程序:“sqlite”、“postgres”、“mysql”、“oracle”、“cockroachdb”。此参数也可以用作关键字参数。

  • args – 数据库驱动程序所需的参数。

  • kwargs – 数据库驱动程序所需的参数。

bind() 调用期间,Pony 尝试建立到数据库的测试连接。如果指定的参数不正确或数据库不可用,将引发异常。在建立到数据库的连接后,Pony 会检索数据库的版本并将连接返回到连接池。

该方法只能对数据库对象调用一次。对同一数据库的任何后续调用将引发 TypeError('Database object was already bound to ... provider') 异常。

db.bind('sqlite', ':sharedmemory:')
db.bind('sqlite', 'filename', create_db=True)
db.bind('postgres', user='', password='', host='', database='')
db.bind('mysql', host='', user='', passwd='', db='')
db.bind('oracle', 'user/password@dsn')
db.bind('cockroach', user='', password='', host='',
        database='', sslmode='disable')

您也可以使用关键字参数来传递参数

db.bind(provider='sqlite', filename=':sharedmemory:')
db.bind(provider='sqlite', filename='db.sqlite', create_db=True)
db.bind(provider='postgres', user='', password='', host='', database='')
db.bind(provider='mysql', host='', user='', passwd='', db='')
db.bind(provider='oracle', user='', password='', dsn='')
db.bind(provider='cockroach', user='', password='', host='',
        database='', sslmode='disable')

这允许将这些参数保存在字典中

db_params = dict(provider='postgres', host='...', port=...,
                 user='...', password='...')
db.bind(**db_params)
commit()

使用 flush() 方法保存当前 db_session() 中进行的所有更改,并将事务提交到数据库。

您可以在同一个 db_session() 中多次调用 commit()。在这种情况下,db_session() 缓存会在提交后保留缓存的对象。缓存将在 db_session() 结束或事务回滚时清除。

create_tables()

检查现有的映射,如果实体的表不存在,则创建它们。此外,Pony 会检查外键和索引是否存在,如果它们不存在,则创建它们。

如果您需要在使用 drop_all_tables() 方法删除表后创建表,此方法可能很有用。如果您没有删除表,您可能不需要此方法,因为 Pony 在 generate_mapping() 调用期间检查并创建表。

disconnect()

如果当前线程的数据库连接已打开,则关闭它。

drop_all_tables(with_all_data=False)

删除与当前映射相关的全部表。

参数

with_all_data (bool) – False 表示 Pony 仅在没有表包含任何数据时才删除表。如果至少有一个表不为空,该方法将引发 TableIsNotEmpty 异常,而不会删除任何表。为了删除包含数据的表,您应该设置 with_all_data=True

drop_table(table_name, if_exists=False, with_all_data=False)

删除 table_name 表。

如果您需要删除映射到实体的表,可以使用实体的类方法 drop_table()

参数
  • table_name (str) – 要删除的表的名称,区分大小写。

  • if_exists (bool) – 当 True 时,如果数据库中不存在该表,则不会引发 TableDoesNotExist 异常。

  • with_all_data (bool) – 如果表不为空,则该方法将引发 TableIsNotEmpty 异常。

on_connect(provider=None)

注册每次为给定提供程序建立新连接时将调用的函数。如果未指定提供程序,则该函数将为每个提供程序调用。该函数应在 db.bind(…) 调用之前注册,并且应具有 2 个位置参数

参数
  • db (Database) – 数据库对象

  • connection (DBAPIConnection) – 连接对象

db = Database()

# entities declaration

@db.on_connect(provider='sqlite')
def sqlite_case_sensitivity(db, connection):
    cursor = connection.cursor()
    cursor.execute('PRAGMA case_sensitive_like = OFF')

db.bind(**options)
db.generate_mapping(create_tables=True)

(版本 0.7.6 中新增)

Entity

此属性表示应由映射到特定数据库的所有实体继承的基类。

示例

db = Database()

class Person(db.Entity):
    name = Required(str)
    age = Required(int)
execute(sql, globals=None, locals=None)

执行 SQL 语句。

在执行提供的 SQL 之前,Pony 使用 flush() 方法刷新当前 db_session() 中进行的所有更改。

参数
  • sql (str) – SQL 语句文本。

  • globals (dict) –

  • locals (dict) – 可选参数,其中可以包含包含变量及其值的字典,在查询中使用。

返回值

DBAPI 游标。

示例

cursor = db.execute("""create table Person (
             id integer primary key autoincrement,
             name text,
             age integer
      )""")

name, age = "Ben", 33
cursor = db.execute("insert into Person (name, age) values ($name, $age)")

有关更多信息,请参阅 原始 SQL 部分。

exists(sql, globals=None, locals=None)

检查数据库中是否至少有一行满足查询。

在执行提供的 SQL 之前,Pony 使用 flush() 方法刷新当前 db_session() 中进行的所有更改。

参数
  • sql (str) – SQL 语句文本。

  • globals (dict) –

  • locals (dict) – 可选参数,其中可以包含包含变量及其值的字典,在查询中使用。

返回类型

bool

示例

name = 'John'
if db.exists("select * from Person where name = $name"):
    print "Person exists in the database"
flush()

db_session() 缓存中累积的更改保存到数据库。您可能永远不需要手动调用此方法,因为它将在离开 db_session() 时自动完成。

Pony 始终在执行以下方法之前自动保存缓存中累积的更改:get()exists()execute()commit()select()

generate_mapping(check_tables=True, create_tables=False)

将声明的实体映射到数据库中的相应表。如果需要,创建表、外键引用和索引。

参数
  • check_tables (bool) – 当 True 时,Pony 会进行简单的检查,以确保数据库中的表名和属性名与实体声明相对应。它不会捕获表具有额外列或特定列的类型不匹配的情况。如果要稍后使用 create_tables() 方法生成映射并为您的实体创建表,请将其设置为 False

  • create_tables (bool) – 如果不存在,则创建表、外键引用和索引。Pony 自动生成数据库表和列的名称,但如果您需要,可以覆盖此行为。有关更多详细信息,请参阅 映射自定义 部分。

get(sql, globals=None, locals=None)

从数据库中选择一行或一个值。

get() 方法假定查询返回正好一行。如果查询未返回任何内容,Pony 将引发 RowNotFound 异常。如果查询返回多行,则将引发 MultipleRowsFound 异常。

在执行提供的 SQL 之前,Pony 使用 flush() 方法刷新当前 db_session() 中进行的所有更改。

参数
  • sql (str) – SQL 语句文本。

  • globals (dict) –

  • locals (dict) – 可选参数,其中可以包含包含变量及其值的字典,在查询中使用。

返回值

元组或值。如果您的请求返回很多列,您可以将 get() 方法的返回值元组分配给一个变量,并像在 select() 方法中描述的那样使用它。

示例

id = 1
age = db.get("select age from Person where id = $id")

name, age = db.get("select name, age from Person where id = $id")
get_connection()

返回活动数据库连接。如果您想直接使用 DBAPI 接口,这将很有用。这与 ORM 本身使用的连接相同。在离开 db_session() 上下文或数据库事务回滚时,连接将被重置并返回到连接池。此连接只能在获取连接的 db_session() 范围内使用。

返回值

DBAPI 连接。

global_stats

此属性保留字典,其中从所有线程聚合执行的 SQL 查询的统计信息。此字典的键是 SQL 语句,值是 QueryStat 类的对象。

insert(table_name|entity, returning=None, **kwargs)

将新行插入表中。此命令绕过标识映射缓存,可用于在需要创建大量对象且不在同一事务中读取它们时提高性能。您也可以使用 execute() 方法来实现此目的。如果您需要在同一事务中使用这些对象,最好创建实体实例并让 Pony 将它们保存到数据库中。

参数
  • table_name|entity (str) – 将插入数据的表的名称。名称区分大小写。您可以使用 entity 类而不是 table_name。在这种情况下,Pony 将插入与 entity 关联的表中。

  • returning (str) – 保存自动生成的 primary key 的列的名称。如果您希望 insert() 方法返回由数据库生成的 value,则应指定 primary key 列的名称。

  • kwargs (dict) – 在查询中使用的命名参数。

示例

new_id = db.insert("Person", name="Ben", age=33, returning='id')
last_sql

保留最后一个 SQL 语句文本的只读属性。它可用于调试。

local_stats

这是一个字典,它保留当前线程的 SQL 查询统计信息。此字典的键是 SQL 语句,值是 QueryStat 类的对象。

merge_local_stats()

将当前线程的统计信息合并到全局统计信息中。您可以在 HTTP 请求处理结束时调用此方法。

当您调用此方法时,local_stats 的值将合并到 global_stats 中,并且 local_stats 将被清除。

在 Web 应用程序中,您可以在完成处理 HTTP 请求后调用此方法。这样,global_stats 属性将包含整个应用程序的统计信息。

rollback()

回滚当前事务并清除 db_session() 缓存。

select(sql, globals=None, locals=None)

在数据库中执行 SQL 语句并返回元组列表。

参数
  • sql (str) – SQL 语句文本。

  • globals (dict) –

  • locals (dict) – 可选参数,其中可以包含包含变量及其值的字典,在查询中使用。

返回值

元组列表。

示例

result = select("select * from Person")

如果查询返回多个列,并且表列的名称是有效的 Python 标识符,那么您可以将它们作为属性访问。

for row in db.select("name, age from Person"):
    print row.name, row.age

支持的数据库

SQLite

使用 SQLite 数据库是使用 Pony 最简单的方法,因为无需单独安装数据库系统 - SQLite 数据库系统包含在 Python 发行版中。对于想要在交互式 shell 中尝试 Pony 的初学者来说,这是一个完美的选择。为了绑定 Database 对象到 SQLite 数据库,您可以执行以下操作

db.bind(provider='sqlite', filename='db.sqlite', create_db=False)
db.bind(provider, filename, create_db=False, timeout=5.0)
参数
  • provider (str) – 对于 SQLite 数据库,应为 ‘sqlite’。

  • filename (str) – SQLite 将存储数据的文件的名称。文件名可以是绝对路径或相对路径。如果您指定相对路径,则该路径将附加到创建此数据库的 Python 文件的目录路径(而不是当前工作目录)。这是因为有时程序员无法控制当前工作目录(例如在 mod_wsgi 应用程序中)。这种方法允许程序员创建由独立模块组成的应用程序,其中每个模块都可以使用单独的数据库。在交互式 shell 中工作时,Pony 要求您始终指定存储文件的绝对路径。

  • create_db (bool) – True 表示如果此文件名不存在,Pony 将尝试创建数据库。如果此文件名存在,Pony 将使用此文件。

  • timeout (float) – timeout 参数指定连接在引发异常之前应等待锁消失多长时间。默认值为 5.0(五秒)。(版本 0.7.3 中新增)

通常 SQLite 数据库存储在磁盘上的文件中,但它也可以完全存储在内存中。这是一种在交互式 shell 中使用 Pony 时创建 SQLite 数据库的便捷方法,但您应该记住,整个内存数据库将在程序退出时丢失。

为了绑定到内存数据库,您应该指定 :memory: 而不是文件名。

db.bind(provider='sqlite', filename=':memory:')

要从不同的线程使用同一个内存 SQLite 数据库,您应该指定 :sharedmemory: 代替。

db.bind(provider='sqlite', filename=':sharedmemory:')

创建内存数据库时,无需使用参数 create_db

注意

默认情况下,SQLite 不会检查外键约束。Pony 始终通过从版本 0.4.9 开始发送命令 PRAGMA foreign_keys = ON; 来启用外键支持。

PostgreSQL

Pony 使用 psycopg2 驱动程序来使用 PostgreSQL。为了将 Database 对象绑定到 PostgreSQL,请使用以下行

db.bind(provider='postgres', user='', password='', host='', database='')

所有跟随 Pony 数据库提供程序名称的参数都将传递给 psycopg2.connect() 方法。查看 psycopg2.connect 文档 了解您可以传递给此方法的其他参数。

MySQL

db.bind(provider='mysql', host='', user='', passwd='', db='')

Pony 尝试使用 MySQLdb 驱动程序来使用 MySQL。如果无法导入此模块,Pony 将尝试使用 pymysql。有关这些驱动程序的更多信息,请参阅 MySQLdbpymysql 文档。

Oracle

db.bind(provider='oracle', user='', password='', dsn='')

Pony 使用 cx_Oracle 驱动程序连接到 Oracle 数据库。有关用于创建与 Oracle 数据库连接的参数的更多信息,请参见 此处

CockroachDB

Pony 使用 psycopg2 驱动程序来使用 CockroachDB。为了将 Database 对象绑定到 CockroachDB,请使用以下行

db.bind(provider='cockroach', user='', password='', host='', database='',
        sslmode='disable')

所有跟随 Pony 数据库提供程序名称的参数都将传递给 CockroachDB 驱动程序。

如果您想使用安全连接到 CockroachDB,则必须指定其他参数

db.bind(provider='cockroach', user='', password='', host='', database='',
        port=26257, sslmode='require', sslrootcert='certs/ca.crt',
        sslkey='certs/client.maxroach.key', sslcert='certs/client.maxroach.crt')

事务和 db_session

@db_session(allowed_exceptions=[], immediate=False, optimistic=True, retry=0, retry_exceptions=[TransactionError], serializable=False, strict=False, sql_debug=None, show_values=None)

用于建立数据库会话。

参数
  • allowed_exceptions (list) – 当发生时不会导致事务回滚的异常列表。对于某些使用异常触发 HTTP 重定向的 Web 框架可能很有用。

  • immediate (bool) – 告诉 Pony 何时开始与数据库进行事务。某些数据库(例如 SQLite、Postgres)仅在将修改查询发送到数据库(UPDATE、INSERT、DELETE)时才开始事务,而不会为 SELECT 开始事务。如果您需要在 SELECT 上开始事务,那么您应该设置 immediate=True。通常不需要更改此参数。

  • optimistic (bool) – 默认情况下为 True。当 optimistic=False 时,不会在该 db_session 中的查询中添加乐观检查(版本 0.7.3 中新增)

  • retry (int) – 指定当前事务提交的尝试次数。此参数只能与 @db_session 装饰器一起使用。装饰后的函数不应显式调用 commit()rollback() 函数。当指定此参数时,Pony 会捕获 TransactionError 异常(及其所有子类)并重新启动当前事务。默认情况下,Pony 只捕获 TransactionError 异常,但可以使用 retry_exceptions 参数修改此列表。

  • retry_exceptions (list|callable) – 将导致事务重新启动的异常列表。默认情况下,此参数等于 [TransactionError]。另一个选项是使用返回布尔值的调用函数。此调用函数接收一个参数 - 异常对象。如果此调用函数返回 True,则事务将重新启动。

  • serializable (bool) – 允许为事务设置 SERIALIZABLE 隔离级别。

  • strict (bool) – 当 True 时,退出 db_session 时将清除缓存。如果您尝试在会话结束后访问对象,您将获得 pony.orm.core.DatabaseSessionIsOver 异常。通常,Pony 强烈建议您只在 db_session 中使用实体对象。但一些 Pony 用户希望即使在 db_session 结束后也能以只读模式访问提取的​​对象。为了提供此功能,默认情况下,Pony 不会在退出 db_session 时清除缓存。这可能很方便,但同时,这可能需要更多内存来将从数据库中提取的所有对象保存在缓存中。

  • sql_debug (bool) – 当 sql_debug=True 时 - 将 SQL 语句记录到控制台或日志文件。当 sql_debug=False 时 - 抑制日志记录,如果它已通过 set_sql_debug() 在全局范围内设置。默认值 None 表示它不会更改全局调试模式。(version 0.7.3 中新增)

  • show_values (bool) – 当 True 时,除了 SQL 文本之外,还会记录查询参数。(version 0.7.3 中新增)

可以用作装饰器或上下文管理器。当会话结束时,它将执行以下操作

  • 如果数据已更改且没有发生异常,则提交事务,否则回滚事务。

  • 将数据库连接返回到连接池。

  • 清除身份映射缓存。

如果您忘记在必要时指定 db_session,Pony 将引发 TransactionError: db_session is required when working with the database 异常。

当您使用 Python 的交互式 shell 时,您无需担心数据库会话,因为 Pony 会自动维护它。

如果您尝试访问不在 db_session 范围内的数据库中加载的实例属性,您将获得 DatabaseSessionIsOver 异常。发生这种情况是因为此时连接到数据库已返回到连接池,事务已关闭,我们无法向数据库发送任何查询。

当 Pony 从数据库中读取对象时,它会将这些对象放入身份映射中。稍后,当您更新对象的属性、创建或删除对象时,更改将首先累积在身份映射中。更改将在事务提交时或在调用以下函数之前保存到数据库中:get()exists()commit()select()

用作装饰器的示例

@db_session
def check_user(username):
    return User.exists(username=username)

用作上下文管理器的示例

def process_request():
    ...
    with db_session:
        u = User.get(username=username)
        ...

事务隔离级别和数据库特性

隔离是一个属性,它定义了何时由一个事务进行的更改对其他并发事务可见 隔离级别。ANSI SQL 标准定义了四个隔离级别

  • READ UNCOMMITTED - 最不安全的级别

  • READ COMMITTED

  • REPEATABLE READ

  • SERIALIZABLE - 最安全的级别

使用 SERIALIZABLE 级别时,每个事务都会将数据库视为在事务开始时创建的快照。此级别提供最高的隔离性,但它比其他级别需要更多资源。

这就是大多数数据库默认使用较低隔离级别的原因,这允许更大的并发性。默认情况下,Oracle 和 PostgreSQL 使用 READ COMMITTED,MySQL - REPEATABLE READ。SQLite 仅支持 SERIALIZABLE 级别,但 Pony 模拟 READ COMMITTED 级别以允许更大的并发性。

如果您希望 Pony 使用 SERIALIZABLE 隔离级别来处理事务,您可以通过将 serializable=True 参数指定给 db_session() 装饰器或 db_session() 上下文管理器来实现

@db_session(serializable=True)
def your_function():
    ...

READ COMMITTED 与 SERIALIZABLE 模式

在 SERIALIZABLE 模式下,您始终有机会遇到“由于并发更新而无法序列化访问”错误,并且必须重试事务直到成功。当您对写入事务使用 SERIALIZABLE 模式时,始终需要在应用程序中编写重试循环。

在 READ COMMITTED 模式下,如果您想避免并发事务更改相同数据,您应该使用 SELECT FOR UPDATE。但这样有可能出现 数据库死锁 - 一个事务等待被另一个事务锁定的资源的情况。如果您的事务发生死锁,您的应用程序需要重新启动事务。因此,无论哪种方式,您最终都需要一个重试循环。如果您将 retry 参数指定给 db_session() 装饰器(但不是 db_session() 上下文管理器),Pony 可以自动重新启动事务

@db_session(retry=3)
def your_function():
    ...

SQLite

使用 SQLite 时,Pony 的行为类似于 PostgreSQL:当事务启动时,选择将在自动提交模式下执行。此模式的隔离级别等效于 READ COMMITTED。这样,并发事务可以同时执行,而不会有死锁的风险(sqlite3.OperationalError: database is locked 不会在 Pony ORM 中出现)。当您的代码发出非选择语句时,Pony 会开始一个事务,并且所有后续的 SQL 语句将在该事务中执行。事务将具有 SERIALIZABLE 隔离级别。

PostgreSQL

PostgreSQL 默认使用 READ COMMITTED 隔离级别。PostgreSQL 还支持自动提交模式。在此模式下,每个 SQL 语句都在单独的事务中执行。当您的应用程序只是从数据库中选择数据时,自动提交模式可能更有效,因为无需发送开始和结束事务的命令,数据库会自动为您执行此操作。从隔离的角度来看,自动提交模式与 READ COMMITTED 隔离级别没有区别。在这两种情况下,您的应用程序都会看到此时已提交的数据。

当您的应用程序需要通过多个 INSERT、UPDATE 或 DELETE SQL 语句来修改数据以提供数据更新的原子性时,Pony 会自动从自动提交模式切换并开始显式事务。

MySQL

MySQL 默认使用 REPEATABLE READ 隔离级别。Pony 在 MySQL 中不使用自动提交模式,因为在这里使用它没有好处。事务从发送到数据库的第一个 SQL 语句开始,即使这是一个 SELECT 语句。

Oracle

Oracle 默认使用 READ COMMITTED 隔离级别。Oracle 没有自动提交模式。事务从发送到数据库的第一个 SQL 语句开始,即使这是一个 SELECT 语句。

CockroachDB

CocrkoachDB 使用在数据库级别实现的乐观事务。应用程序应该处理代码 40001 的错误以及以字符串“retry transaction”开头的错误消息,方法是重试事务代码,在此处查看更多信息

PonyORM 可以自动处理该逻辑。如果您将 retry=N 选项指定给 db_session 装饰器,那么 PonyORM 将自动尝试 N 次重试用 db_session 装饰的代码。请注意,db_session 应该指定为装饰器,而不是上下文管理器,因为 Python 中的上下文管理器无法重试代码块。

实体定义

实体是 Python 类,它将对象的 state 存储在数据库中。每个实体实例对应于数据库表中的一行。通常,实体代表现实世界中的对象(例如 Customer、Product)。

实体属性

实体属性在实体类中使用以下语法指定为类属性

class EntityName(inherits_from)
    attr_name = attr_kind(attr_type, attr_options)

例如

class Person(db.Entity):
    id = PrimaryKey(int, auto=True)
    name = Required(str)
    age = Optional(int)

属性种类

每个实体属性可以是以下种类之一

  • Required - 必须始终具有值

  • Optional - 值是可选的

  • PrimaryKey - 定义主键属性

  • Set - 表示集合,用于“多对多”关系

  • Discriminator - 用于实体继承

可选字符串属性

对于大多数数据类型,当未将值分配给属性时,将使用 None。但是,当未将值分配给字符串属性时,Pony 会使用空字符串而不是 None。这比在数据库中将空字符串存储为 NULL 更实用。大多数框架都以这种方式运行。此外,与 NULL 不同,空字符串可以被索引以加快搜索速度。如果您尝试将 None 分配给此类可选字符串属性,您将获得 ConstraintError 异常。

您可以使用 nullable=True 选项更改此行为。在这种情况下,将有可能在同一列中存储空字符串和 NULL 值,但这很少需要。

Oracle 数据库将空字符串视为 NULL 值。因此,Oracle 中的所有 Optional 属性都将 nullable 自动设置为 True

如果可选字符串属性用作唯一键或复合唯一键的一部分,它将始终将 nullable 自动设置为 True

复合主键和二级键

Pony 完全支持复合键。为了声明复合主键,您需要将键的所有部分指定为 Required,然后将它们组合成复合主键

class Example(db.Entity):
    a = Required(int)
    b = Required(str)
    PrimaryKey(a, b)

为了声明二级复合键,您需要像往常一样声明属性,然后使用 composite_key 指令将它们组合起来

class Example(db.Entity):
    a = Required(str)
    b = Optional(int)
    composite_key(a, b)

在数据库中,composite_key(a, b) 将表示为 UNIQUE ("a", "b") 约束。

复合索引

使用 composite_index() 指令,您可以为加快数据检索速度创建复合索引。它可以组合两个或多个属性

class Example(db.Entity):
    a = Required(str)
    b = Optional(int)
    composite_index(a, b)

复合索引可以包含用于继承的鉴别器属性。

使用 composite_index(),您可以创建非唯一索引。为了定义唯一索引,请使用上面描述的 composite_key() 函数。

属性数据类型

Pony 支持以下属性类型

  • str

  • unicode

  • int

  • float

  • Decimal

  • datetime

  • date

  • time

  • timedelta

  • bool

  • buffer - 用于 Python 2 和 3 中的二进制数据

  • bytes - 用于 Python 3 中的二进制数据

  • LongStr - 用于大型字符串

  • LongUnicode - 用于大型字符串

  • UUID

  • Json - 用于映射到本机数据库 JSON 类型

  • IntArray

  • StrArray

  • FloatArray

注意

IntArrayStrArrayFloatArray 类型仅在 PostgreSQL 和 SQLite 中受支持。

您还可以指定另一个实体作为属性类型来定义两个实体之间的关系。

Python 2 和 3 中的字符串

如您所知,Python 3 在字符串方面与 Python 2 有所不同。Python 2 提供两种字符串类型 - str(字节字符串)和 unicode(unicode 字符串),而在 Python 3 中,str 类型表示 unicode 字符串,而 unicode 则被删除了。

在 0.6 版发布之前,Pony 将 strunicode 属性存储为数据库中的 unicode,但对于 str 属性,它必须在从数据库读取时将 unicode 转换为字节字符串。从 Pony 0.6 版开始,Python 2 中 str 类型的属性的行为就像它们被声明为 unicode 属性一样。现在,无论您指定 str 还是 unicode 作为属性类型,都没有区别 - 您将在 Python 和数据库中拥有 unicode 字符串。

从 Pony 0.6 版开始,添加了对 Python 3 的支持,我们建议使用 strLongStr 类型,而不是 unicodeLongUnicodeLongStrLongUnicode 在数据库中存储为 CLOB。

LongUnicodeLongStr 也是如此。 LongStr 现在是 LongUnicode 的别名。此类型在 Python 和数据库中使用 unicode。

attr1 = Required(str)
# is the same as
attr2 = Required(unicode)

attr3 = Required(LongStr)
# is the same as
attr4 = Required(LongUnicode)

Python 2 和 3 中的 Buffer 和 Bytes 类型

如果您需要在 Python 2 中表示字节序列,可以使用 buffer 类型。在 Python 3 中,您应该为此目的使用 bytes 类型。 bufferbytes 类型在数据库中存储为二进制(BLOB)类型。

在 Python 3 中,buffer 类型已经消失,Pony 使用了在 Python 3 中添加的 bytes 类型来表示二进制数据。但为了向后兼容,我们仍然保留 buffer 作为 Python 3 中 bytes 类型的别名。如果您从 pony.orm 中导入 *,您也将获得此别名。

如果您想编写可以在 Python 2 和 Python 3 上运行的代码,您应该使用 buffer 类型来表示二进制属性。如果您的代码仅供 Python 3 使用,则可以使用 bytes 代替。

attr1 = Required(buffer) # Python 2 and 3

attr2 = Required(bytes) # Python 3 only

如果我们可以在 Python 2 中使用 bytes 类型作为 buffer 的别名,那将很酷,但不幸的是,这是不可能的,因为 Python 2.6 将 bytes 添加为 str 类型的同义词

属性选项

属性选项可以在属性定义期间作为位置参数和关键字参数指定。

最大字符串长度

字符串类型可以接受一个位置参数,该参数指定数据库中此列的最大长度。

class Person(db.Entity):
    name = Required(str, 40)   #  VARCHAR(40)

您也可以使用 max_len 选项。

class Person(db.Entity):
    name = Required(str, max_len=40)   #  VARCHAR(40)

小数位数和精度

对于 Decimal 类型,您可以指定精度和小数位数。

class Product(db.Entity):
    price = Required(Decimal, 10, 2)   #  DECIMAL(10, 2)

您也可以使用 precisionscale 选项。

class Product(db.Entity):
    price = Required(Decimal, precision=10, scale=2)   #  DECIMAL(10, 2)

如果您没有指定 precisionscale 参数,Pony 默认使用 Decimal(precision=12, scale=2) 值。

日期时间、时间和时间差精度

datetimetime 类型接受一个位置参数,该参数指定列的精度。默认情况下,对于大多数数据库,它等于 6。

对于 MySQL 数据库,默认值为 0。在 MySQL 5.6.4 版本之前,DATETIMETIME根本无法存储小数秒。从 5.6.4 版本开始,如果您在属性定义期间将精度设置为 6,则可以存储小数秒。

class Action(db.Entity):
    dt = Required(datetime, 6)

同样,使用 precision 选项。

class Action(db.Entity):
    dt = Required(datetime, precision=6)

关键字参数选项

其他属性选项可以作为关键字参数设置。例如

class Customer(db.Entity):
    email = Required(str, unique=True)

下面您可以找到可用选项的列表。

auto

(bool) 只能用于主键属性。如果 auto=True,则此属性的值将使用数据库的增量计数器或序列自动分配。

autostrip

(bool) 自动删除字符串属性中前导和尾随空格字符。类似于 Python string.strip() 函数。默认值为 True

cascade_delete

(bool) 控制相关对象的级联删除。 True 表示 Pony 始终执行级联删除,即使另一端定义为 OptionalFalse 表示 Pony 从不为此关系执行级联删除。如果关系在另一端定义为 Required 并且 cascade_delete=False,则 Pony 在删除尝试时引发 ConstraintError 异常。 另请参阅

column

(str) 指定数据库表中用于映射的列的名称。默认情况下,Pony 使用属性名称作为数据库中的列名称。

columns

(list) 指定数据库表中用于映射复合属性的列名。

default

(numeric|str|function) 允许为属性指定默认值。Pony 在 Python 中处理默认值,它不会向列定义添加 SQL DEFAULT 子句。这是因为默认表达式不仅可以是常量,还可以是任何任意的 Python 函数。例如

import uuid
from pony.orm import *

db = Database()

class MyEntity(db.Entity):
    code = Required(uuid.UUID, default=uuid.uuid4)

如果您需要在数据库中设置默认值,则应使用 sql_default 选项。

fk_name

(str) 适用于 RequiredOptional 关系属性,允许指定数据库中外键的名称。

index

(bool|str) 允许控制此列的索引创建。 index=True - 将使用默认名称创建索引。 index='index_name' - 使用指定的名称创建索引。 index=False - 跳过索引创建。如果未指定“index”选项,则 Pony 仍会使用默认名称为外键创建索引。

lazy

(bool) 当为 True 时,Pony 会在加载对象时延迟加载属性值。在您尝试直接访问此属性之前,不会加载该值。默认情况下,lazyLongStrLongUnicode 设置为 True,而对于所有其他类型则设置为 False

max

(numeric) 允许为数字属性(int、float、Decimal)指定最大允许值。如果您尝试分配大于指定最大值的 value,您将获得 ValueError 异常。

max_len

(int) 设置字符串属性的最大长度。

min

(numeric) 允许为数字属性(int、float、Decimal)指定最小允许值。如果您尝试分配小于指定最小值的 value,您将获得 ValueError 异常。

nplus1_threshold

(int) 此参数用于微调用于 N+1 问题解决方案的阈值。

nullable

(bool) True 允许列在数据库中为 NULL。您很可能不需要指定此选项,因为 Pony 默认情况下会将其设置为最合适的值。

optimistic

(bool) True 表示此属性将用于自动乐观检查, 请参阅乐观并发控制 部分。默认情况下,此选项对于所有属性(除 float 类型属性外)都设置为 True - 对于 float 类型属性,默认情况下它设置为 False

另请参阅 volatile 选项

precision

(int) 设置 Decimaltimetimedeltadatetime 属性类型的精度。

py_check

(function) 允许指定一个函数,该函数将用于在将值分配给属性之前检查该值。该函数应返回 TrueFalse。它也可以在检查失败时引发 ValueError 异常。

class Student(db.Entity):
    name = Required(str)
    gpa = Required(float, py_check=lambda val: val >= 0 and val <= 5)
reverse

(str) 指定另一端应用于关系的属性名称。如果两个实体之间存在多个关系,则可能需要它。

reverse_column

(str) 用于对称关系,以指定中间表中数据库列的名称。

reverse_columns

(list) 如果实体具有复合主键,则用于对称关系。允许您指定中间表中数据库列的名称。

scale

(int) 设置 Decimal 属性类型的比例。

size

(int) 对于 int 类型,您可以使用 size 关键字指定数据库中应使用的整数类型的尺寸。此参数接收用于在数据库中表示整数的位数。允许的值为 8、16、24、32 和 64

attr1 = Required(int, size=8)   # 8 bit - TINYINT in MySQL
attr2 = Required(int, size=16)  # 16 bit - SMALLINT in MySQL
attr3 = Required(int, size=24)  # 24 bit - MEDIUMINT in MySQL
attr4 = Required(int, size=32)  # 32 bit - INTEGER in MySQL
attr5 = Required(int, size=64)  # 64 bit - BIGINT in MySQL

您可以使用 unsigned 参数指定属性是无符号的

attr1 = Required(int, size=8, unsigned=True) # TINYINT UNSIGNED in MySQL

unsigned 参数的默认值为 False。如果 unsigned 设置为 True,但未提供 size,则 size 假定为 32 位。

如果当前数据库不支持指定的属性大小,则使用下一个更大的大小。例如,PostgreSQL 没有 MEDIUMINT 数字类型,因此 INTEGER 类型将用于大小为 24 的属性。

只有 MySQL 实际上支持无符号类型。对于其他数据库,该列将使用有符号数字类型,该类型可以保存指定无符号类型的所有有效值。例如,在 PostgreSQL 中,大小为 16 的无符号属性将使用 INTEGER 类型。大小为 64 的无符号属性只能在 MySQL 和 Oracle 中表示。

当指定大小后,Pony 会自动为该属性分配 minmax 值。例如,大小为 8 的有符号属性将接收 min 值 -128 和 max 值 127,而大小相同的无符号属性将接收 min 值 0 和 max 值 255。如果需要,您可以用自己的值覆盖 minmax,但这些值不应超过大小隐含的范围。

从 Pony 版本 0.6 开始,long 类型已弃用,如果您想在数据库中存储 64 位整数,则需要使用 int,并使用 size=64。如果您没有指定 size 参数,Pony 将使用特定数据库的默认整数类型。

sequence_name

(str) 允许指定用于 PrimaryKey 属性的序列名称。仅限 Oracle 数据库。

sql_default

(str) 此选项允许指定将包含在 CREATE TABLE SQL 命令中的默认 SQL 文本。例如

class MyEntity(db.Entity):
    created_at = Required(datetime, sql_default='CURRENT_TIMESTAMP')
    closed = Required(bool, default=True, sql_default='1')

当您有一个 Required 属性并且它的值将在 INSERT 命令期间在数据库中计算(例如,通过触发器)时,指定 sql_default=True 可能很方便。默认情况下为 None

sql_type

(str) 为该列设置特定的 SQL 类型。

unique

(bool) 如果为 True,则数据库将检查此属性的值是否唯一。

unsigned

(bool) 允许在数据库中创建无符号类型。还会检查分配的值是否为正。

table

(str) 仅用于多对多关系,以指定中间表的名称。

volatile

(bool) 通常您在 Python 中指定属性的值,Pony 将此值存储在数据库中。但有时您可能希望在数据库中有一些逻辑来更改列的值。例如,您可以在数据库中有一个触发器,它更新最后一个对象的修改时间戳。在这种情况下,您希望 Pony 在发送到数据库的对象更新时忘记属性的值,并在下次访问尝试时从数据库中读取它。设置 volatile=True 以让 Pony 知道此属性可以在数据库中更改。

volatile=True 选项可以与 sql_default 选项结合使用,如果此属性的值将由数据库创建和更新。

如果您在当前事务中使用的属性的值被另一个事务更改,则可能会收到异常 UnrepeatableReadError: Value ... was updated outside of current transaction。Pony 会通知您,因为这种情况可能会破坏应用程序的业务逻辑。如果您不希望 Pony 保护您免受此类并发修改,您可以为该属性设置 volatile=True。这将关闭乐观并发控制。

另请参阅 乐观选项.

集合属性方法

多对多属性具有提供查询数据的便捷方法。您可以将多对多关系属性视为常规的 Python 集合,并使用标准操作,如 innot inlen。Pony 还提供了以下方法

class Set
__len__()

返回集合中的对象数量。如果集合未加载到缓存中,此方法会先将所有集合实例加载到缓存中,然后返回对象数量。如果您要遍历对象并且需要将它们加载到缓存中,请使用此方法。如果您不需要将集合加载到内存中,可以使用 count() 方法。

>>> p1 = Person[1]
>>> Car[1] in p1.cars
True
>>> len(p1.cars)
2
add(item|iter)

将实例添加到集合中,并在实体实例之间建立双向关系

photo = Photo[123]
photo.tags.add(Tag['Outdoors'])

现在,具有主键 123 的 Photo 实体的实例与 Tag['Outdoors'] 实例之间存在关系。 Tag['Outdoors'] 实例的属性 photos 也包含对 Photo[123] 的引用。

您还可以通过将标签列表传递给 add() 方法来一次建立多个关系

photo.tags.add([Tag['Party'], Tag['New Year']])
clear()

从集合中删除所有项目,这意味着断开实体实例之间的关系。

copy()

返回一个 Python set 对象,其中包含与给定集合相同的项目。

count(distinct=False)

返回集合中的对象数量。此方法不会将集合实例加载到缓存中,而是生成一个 SQL 查询,该查询从数据库返回对象数量。如果您要使用集合对象(遍历集合或更改对象属性),您可能需要使用 __len__() 方法。

create(**kwargs)

创建并返回相关实体的实例,并与其建立关系

new_tag = Photo[123].tags.create(name='New tag')

等效于以下内容

new_tag = Tag(name='New tag')
Photo[123].tags.add(new_tag)
drop_table(with_all_data=False)

删除为建立多对多关系而创建的中间表。如果表不为空且 with_all_data=False,则该方法会引发 TableIsNotEmpty 异常,并且不会删除任何内容。设置 with_all_data=True 允许您即使表不为空也可以删除它。

class Product(db.Entity):
    tags = Set('Tag')

class Tag(db.Entity):
    products = Set(Product)

Product.tags.drop_table(with_all_data=True) # removes the intermediate table
is_empty()

检查集合是否为空。如果至少存在一个关系,则返回 False;如果此属性没有关系,则返回 True

select(g for g in Group if not g.students.is_empty())
filter()

从集合中选择对象。方法名 select()filter() 是同义词。示例

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

从数据库加载所有相关对象。

order_by(attr|lambda)

返回一个有序集合。

g.students.order_by(Student.name).page(2, pagesize=3)
g.students.order_by(lambda s: s.name).limit(3, offset=3)
sort_by(attr|lambda)

返回一个有序集合。对于集合,sort_by 方法的工作方式与 order_by() 相同。

g.students.sort_by(Student.name).page(2, pagesize=3)
g.students.sort_by(lambda s: s.name).limit(3, offset=3)
page(pagenum, pagesize=10)

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

g.students.order_by(Student.name).page(2, pagesize=3)
g.students.order_by(lambda s: s.name).limit(3, offset=3)
random(limit)

从集合中返回一定数量的随机对象。

g = Group[101]
g.students.random(2)
remove(item|iter)

从集合中删除一个或多个项目,从而断开实体实例之间的关系。

select()

从集合中选择对象。方法名 select()filter() 是同义词。示例

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

实体选项

composite_index(attrs)

从多个属性组合索引。 Link.

composite_key(attrs)

从多个属性组合二级键。 Link.

_discriminator_

为实体指定鉴别器值。有关更多信息,请参见 实体继承 部分。

PrimaryKey(attrs)

从多个属性组合主键。 Link.

_table_

指定数据库中映射表的名称。有关更多信息,请参见 映射自定义 部分。

_table_options_

此处指定的所有参数都将作为纯文本添加到 CREATE TABLE 命令的末尾。示例

class MyEntity(db.Entity):
    id = PrimaryKey(int)
    foo = Required(str)
    bar = Optional(int)

    _table_options_ = {
        'ENGINE': 'InnoDB',
        'TABLESPACE': 'my_tablespace',
        'ENCRYPTION': "'N'",
        'AUTO_INCREMENT': 10
    }

实体钩子

有时您可能需要在实体实例将要创建、更新或删除到数据库之前或之后执行操作。为此,您可以使用实体钩子。

以下是可用钩子的列表

after_delete()

在实体实例从数据库中删除后调用。

after_insert()

在将行插入数据库后调用。

after_update()

在实例在数据库中更新后调用。

before_delete()

在从数据库中删除实体实例之前调用。

before_insert()

仅在将新创建的对象插入数据库之前调用。

before_update()

在更新数据库中的实体实例之前调用。

为了使用钩子,您需要定义一个具有钩子名称的实体方法

class Message(db.Entity):
    title = Required(str)
    content = Required(str)

    def before_insert(self):
        print("Before insert! title=%s" % self.title)

每个钩子方法都会收到要修改的对象的实例。您可以在交互模式下检查它的工作原理

>>> m = Message(title='First message', content='Hello, world!')
>>> commit()
Before insert! title=First message

INSERT INTO "Message" ("title", "content") VALUES (?, ?)
[u'First message', u'Hello, world!']

实体方法

class Entity
classmethod __getitem__()

返回按其主键选择的实体实例。如果不存在这样的对象,则引发 ObjectNotFound 异常。示例

p = Product[123]

对于具有复合主键的实体,请在主键值之间使用逗号

item = OrderItem[123, 456]

如果具有指定主键的对象已加载到 db_session() 缓存中,Pony 会从缓存中返回对象,而不会向数据库发送查询。

delete()

删除实体实例。该实例将被标记为已删除,并在flush()函数执行时从数据库中删除,该函数在提交当前事务时自动执行,即退出最外层的db_session()时或在向数据库发送下一个查询之前执行。

Order[123].delete()
classmethod describe()

返回包含实体声明的字符串。

>>> print(OrderItem.describe())

class OrderItem(Entity):
    quantity = Required(int)
    price = Required(Decimal)
    order = Required(Order)
    product = Required(Product)
    PrimaryKey(order, product)
classmethod drop_table(with_all_data=False)

删除与数据库中实体关联的表。如果表不为空且with_all_data=False,则该方法会引发TableIsNotEmpty异常,并且不会删除任何内容。设置with_all_data=True允许您即使表不为空也能删除它。

如果您需要删除为多对多关系创建的中间表,则必须调用关系属性的select()方法。

classmethod exists(*args, **kwargs)

如果存在具有指定条件或属性值的实例,则返回True,否则返回False

Product.exists(price=1000)
Product.exists(lambda p: p.price > 1000)
flush()

将对该对象所做的更改保存到数据库。通常,Pony 会自动保存更改,您不需要自己调用此方法。它可能需要使用的一种情况是,当您想要在提交之前获取具有自动递增主键的新创建对象的 主键值时。

classmethod get(*args, **kwargs)

从数据库中提取一个实体实例。

如果具有指定参数的对象存在,则返回该对象。如果不存在这样的对象,则返回None。如果有多个具有指定参数的对象,则会引发MultipleObjectsFoundError: Multiple objects were found. Use select(...) to retrieve them异常。示例

Product.get(price=1000)
Product.get(lambda p: p.name.startswith('A'))
classmethod get_by_sql(sql, globals=None, locals=None)

通过原始 SQL 选择实体实例。

如果您发现无法使用标准 Pony 查询来表达查询,则始终可以编写自己的 SQL 查询,Pony 将根据查询结果构建实体实例。当 Pony 获取 SQL 查询的结果时,它会分析从数据库游标接收的列名。如果您的查询使用实体表的SELECT * ...,则足以获取构建实体实例所需的属性值。您可以将参数传递到查询中,有关更多信息,请参见使用 select_by_sql() 和 get_by_sql() 方法

classmethod get_for_update(*args, **kwargs, nowait=False, skip_locked=False)

注意

nowaitskip_locked参数是互斥的。

参数
  • nowait (bool) – 阻止操作等待其他事务提交。如果选定的行无法立即锁定,则操作会报告错误,而不是等待。

  • skip_locked (bool) – 在 FOR UPDATE 子句中添加 SKIP LOCKED 选项

使用SELECT ... FOR UPDATE SQL 查询锁定数据库中的行。如果nowait=True,则如果该行已被阻止,则该方法将抛出异常。如果nowait=False,则如果该行已被阻止,则它将等待。

如果您需要对多行使用SELECT ... FOR UPDATE,则应使用for_update()方法。

get_pk()

获取对象的 主键值。

>>> c = Customer[1]
>>> c.get_pk()
1

如果主键是复合主键,则此方法将返回一个包含主键列值的元组。

>>> oi = OrderItem[1,4]
>>> oi.get_pk()
(1, 4)
load(*args)

加载所有延迟和非延迟属性,但不加载集合属性,这些属性尚未从数据库中检索。如果属性已加载,则不会再次加载。您可以指定需要加载的属性列表或其名称。在这种情况下,Pony 将只加载它们

obj.load(Person.biography, Person.some_other_field)
obj.load('biography', 'some_other_field')
classmethod select(lambda=None, **kwargs)

根据 lambda 或关键字参数中指定的条件从数据库中选择对象,或者如果未指定 lambda 函数,则选择所有对象。

select()方法返回Query类的实例。实体实例将在您开始迭代Query对象时从数据库中检索。

此查询示例返回所有价格大于 100 且已订购多次的产品

Product.select(lambda p: p.price > 100 and count(p.order_items) > 1)
Product.select(item_type='TV')

注意

从 0.7.7 版本开始,select 也可以与关键字参数一起使用

classmethod select_by_sql(sql, globals=None, locals=None)

通过原始 SQL 选择实体实例。有关更多信息,请参见使用 select_by_sql() 和 get_by_sql() 方法

classmethod select_random(limit)

选择limit个随机对象。此方法使用比使用ORDER BY RANDOM() SQL 结构更有效的算法。该方法使用以下算法

  1. 从表中确定最大 id。

  2. 在 (0, max_id] 范围内生成随机 id。

  3. 通过这些随机 id 检索对象。如果不存在具有生成的 id 的对象(例如,它已被删除),则选择另一个随机 id 并重试。

重复步骤 2-3,直到检索到指定数量的对象。

即使在处理大量表行时,此算法也不会影响性能。但是,此方法也有一些限制

  • 主键必须是整数类型的连续 id。

  • 现有 id 之间的“间隙”数量(已删除对象的计数)应该相对较小。

如果您的查询没有任何选择特定对象的条件,则可以使用select_random()方法。如果需要此类条件,则可以使用Query.random()方法。

set(**kwargs)

一次为多个对象属性分配新值

Customer[123].set(email='new@example.com', address='New address')

当您想要从字典中分配新值时,此方法也很方便

d = {'email': 'new@example.com', 'address': 'New address'}
Customer[123].set(**d)
to_dict(only=None, exclude=None, with_collections=False, with_lazy=False, related_objects=False)

返回一个包含属性名称及其值的字典。当您需要将对象序列化为 JSON 或其他格式时,可以使用此方法。

默认情况下,此方法不包括集合(多对多关系)和延迟属性。如果属性的值是实体实例,则只会将该对象的 主键添加到字典中。

参数
  • only (list|str) – 如果您只想获取指定的属性,请使用此参数。此参数可以用作第一个位置参数。您可以指定属性名称列表 obj.to_dict(['id', 'name']),用空格分隔的字符串:obj.to_dict('id name'),或用空格和逗号分隔的字符串:obj.to_dict('id, name')

  • exclude (list|str) – 此参数允许您排除指定的属性。属性名称可以与 only 参数相同的方式指定。

  • related_objects (bool) – 默认情况下,所有相关的对象都表示为主键。如果 related_objects=True,则与当前对象具有关系的对象将作为对象添加到结果字典中,而不是它们的主键。如果您想遍历相关对象并递归调用 to_dict() 方法,这将很有用。

  • with_collections (bool) – 默认情况下,结果字典将不包含集合(多对多关系)。如果您将此参数设置为 True,则多对多关系将表示为列表。如果 related_objects=False(默认情况下),则这些列表将包含相关实例的主键。如果 related_objects=True,则多对多集合将表示为对象列表。

  • with_lazy (bool) – 如果 True,则惰性属性(如 BLOB 或使用 lazy=True 声明的属性)将包含在结果字典中。

  • related_objects – 默认情况下,所有相关对象都表示为仅包含其主键的列表。如果您想查看相关对象实例,可以指定 related_objects=True

为了说明此方法的用法,我们将使用 Pony 分发附带的 eStore 示例。让我们获取一个 id=1 的客户对象并将其转换为字典

>>> from pony.orm.examples.estore import *
>>> c1 = Customer[1]
>>> c1.to_dict()

{'address': u'address 1',
'country': u'US',
'email': u'john@example.com',
'id': 1,
'name': u'John Smith',
'password': u'***'}

如果我们不想序列化密码属性,我们可以通过这种方式将其排除

>>> c1.to_dict(exclude='password')

{'address': u'address 1',
'country': u'US',
'email': u'john@example.com',
'id': 1,
'name': u'John Smith'}

如果您想排除多个属性,可以将它们指定为列表:exclude=['id', 'password'] 或作为字符串:exclude='id, password',它与 exclude='id password' 相同。

您还可以使用参数 only 指定要序列化的属性。

>>> c1.to_dict(only=['id', 'name'])

{'id': 1, 'name': u'John Smith'}

>>> c1.to_dict('name email') # 'only' parameter as a positional argument

{'email': u'john@example.com', 'name': u'John Smith'}

默认情况下,集合不包含在结果字典中。如果您想包含它们,可以指定 with_collections=True。您也可以在 only 参数中指定集合属性

>>> c1.to_dict(with_collections=True)

{'address': u'address 1',
'cart_items': [1, 2],
'country': u'USA',
'email': u'john@example.com',
'id': 1,
'name': u'John Smith',
'orders': [1, 2],
'password': u'***'}

默认情况下,所有相关对象(cart_items、orders)都表示为仅包含其主键的列表。如果您想查看相关对象实例,可以指定 related_objects=True

>>> c1.to_dict(with_collections=True, related_objects=True)

{'address': u'address 1',
'cart_items': [CartItem[1], CartItem[2]],
'country': u'USA',
'email': u'john@example.com',
'id': 1,
'name': u'John Smith',
'orders': [Order[1], Order[2]],
'password': u'***'}

查询和函数

以下是 Pony 中定义的顶层函数列表

avg(gen, distinct=None)

返回所有选定属性的平均值。

参数
  • gen (generator) – Python 生成器表达式

  • distinct (bool) – distinct 选项

返回类型

numeric

avg(o.total_price for o in Order)

可以使用 avg() 方法生成等效查询。

between(x, a, b)

此函数将转换为 x BETWEEN a AND b。它等于条件 x >= a AND x <= b

select(p for p in Person if between(p.age, 18, 65))
coalesce(*args)
参数

args (list) – 参数列表

返回列表中的第一个非空表达式。

select(coalesce(p.phone, 'UNKNOWN') for p in Person)
concat(*args)
参数

args (list) – 参数列表

将参数连接成一个字符串。

select(concat(p.first_name, ' ', p.last_name) for p in Person)
commit()

使用 flush() 函数保存当前 db_session() 中进行的所有更改,并将事务提交到数据库。此顶层 commit() 函数调用在当前事务中使用的每个数据库对象的 commit() 方法。

count(gen, distinct=None)

返回与查询条件匹配的对象数量。

参数
  • gen (generator) – Python 生成器表达式

  • distinct (bool) – distinct 选项

返回类型

numeric

count(c for c in Customer if len(c.orders) > 2)

此查询将转换为以下 SQL

SELECT COUNT(*)
FROM "Customer" "c"
LEFT JOIN "Order" "order-1"
  ON "c"."id" = "order-1"."customer"
GROUP BY "c"."id"
HAVING COUNT(DISTINCT "order-1"."id") > 2

可以使用 count() 方法生成等效查询。

delete(gen)

从数据库中删除对象。Pony 将对象加载到内存中,并将逐个删除它们。如果您定义了 before_delete()after_delete(),Pony 将调用它们中的每一个。

参数

gen (generator) – Python 生成器表达式

delete(o for o in Order if o.status == 'CANCELLED')

如果您需要在不将对象加载到内存的情况下删除对象,则应使用 delete() 方法,参数为 bulk=True。在这种情况下,即使为实体定义了钩子,也不会调用它们。

desc(attr)

此函数用于 order_by()sort_by() 中,用于按降序排序。

参数

attr (attribute) – 实体属性

select(o for o in Order).order_by(desc(Order.date_shipped))

相同的示例,使用 lambda

select(o for o in Order).order_by(lambda o: desc(o.date_shipped))
distinct(gen)

当您需要在查询中强制使用 DISTINCT 时,可以使用 distinct() 函数。但这通常没有必要,因为 Pony 会以智能的方式自动添加 DISTINCT 关键字。有关更多信息,请参阅 TODO 章。

参数

gen (generator) – Python 生成器表达式

distinct(o.date_shipped for o in Order)

distinct() 函数的另一种用法是与 sum() 聚合函数一起使用 - 您可以编写

select(sum(distinct(x.val)) for x in X)

以生成以下 SQL

SELECT SUM(DISTINCT x.val)
FROM X x

但在实践中很少使用。

exists(gen, globals=None, locals=None)

如果存在至少一个具有指定条件的实例,则返回 True,否则返回 False

参数
  • gen (generator) – Python 生成器表达式。

  • globals (dict) –

  • locals (dict) – 可选参数,其中可以包含包含变量及其值的字典,在查询中使用。

返回类型

bool

exists(o for o in Order if o.date_delivered is None)
flush()

db_session() 缓存中的所有更改保存到数据库,而不提交它们。它使在 db_session() 缓存中进行的更新对属于当前事务的所有数据库查询可见。

通常 Pony 会自动保存数据库会话缓存中的数据,您不需要自己调用此函数。可能需要它的一个用例是,当您想在提交之前获取具有自动递增主键的新创建对象的主键值时。

此顶层 flush() 函数调用当前事务中使用的每个数据库对象的 flush() 方法。

此函数在执行以下函数之前自动调用:commit()get()exists()select()

get(gen, globals=None, locals=None)

从数据库中提取一个实体实例。

参数
  • gen (generator) – Python 生成器表达式。

  • globals (dict) –

  • locals (dict) – 可选参数,其中可以包含包含变量及其值的字典,在查询中使用。

返回值

如果存在具有指定参数的对象,则返回该对象;如果不存在,则返回 None

如果存在多个具有指定参数的对象,则函数会引发 MultipleObjectsFoundError: Multiple objects were found. Use select(...) to retrieve them 异常。

get(o for o in Order if o.id == 123)

可以使用 get() 方法生成等效的查询。

getattr(object, name[, default])

这是 Python 内置函数,可用于获取查询中的属性值。

示例

attr_name = 'name'
param_value = 'John'
select(c for c in Customer if getattr(c, attr_name) == param_value)
group_concat(gen, sep=',', distinct=False)

(版本 0.7.4 中新增)

返回给定属性的连接字符串。

group_concat(t.title for t in Tag, sep='-')

可以使用 group_concat() 方法生成等效的查询。

注意

查询应仅返回单个属性。此外,在 SQLite 中,您不能同时使用 distinct 和 sep 参数。

JOIN(*args)

用于在 Pony 未自动提供此优化的情况下优化查询。用作提示,告诉 Pony 我们希望使用 SQL JOIN,而不是在 SQL 查询中生成子查询。

select(g for g in Group if max(g.students.gpa) < 4)

select(g for g in Group if JOIN(max(g.students.gpa) < 4))
left_join(gen, globals=None, locals=None)

左连接的结果始终包含来自“左”表的結果,即使连接条件在“右”表中找不到任何匹配记录。

参数
  • gen (generator) – Python 生成器表达式。

  • globals (dict) –

  • locals (dict) – 可选参数,其中可以包含包含变量及其值的字典,在查询中使用。

假设我们需要计算每个客户的订单数量。让我们使用 Pony 分发附带的示例并编写以下查询

from pony.orm.examples.estore import *
populate_database()

select((c, count(o)) for c in Customer for o in c.orders)[:]

它将被转换为以下 SQL

SELECT "c"."id", COUNT(DISTINCT "o"."id")
FROM "Customer" "c", "Order" "o"
WHERE "c"."id" = "o"."customer"
GROUP BY "c"."id"

并返回以下结果

[(Customer[1], 2), (Customer[2], 1), (Customer[3], 1), (Customer[4], 1)]

但是,如果存在没有订单的客户,则此查询将不会选择他们,因为条件 WHERE "c"."id" = "o"."customer" 在 Order 表中找不到任何匹配记录。为了获取所有客户的列表,我们应该使用 left_join() 函数

left_join((c, count(o)) for c in Customer for o in c.orders)[:]
SELECT "c"."id", COUNT(DISTINCT "o"."id")
FROM "Customer" "c"
LEFT JOIN "Order" "o"
  ON "c"."id" = "o"."customer"
GROUP BY "c"."id"

现在,我们将获得所有客户的列表,对于没有订单的客户,订单数量为零

[(Customer[1], 2), (Customer[2], 1), (Customer[3], 1), (Customer[4], 1), (Customer[5], 0)]

我们应该提到,在大多数情况下,Pony 可以理解在哪里需要 LEFT JOIN。例如,相同的查询可以这样编写

select((c, count(c.orders)) for c in Customer)[:]
SELECT "c"."id", COUNT(DISTINCT "order-1"."id")
FROM "Customer" "c"
LEFT JOIN "Order" "order-1"
  ON "c"."id" = "order-1"."customer"
GROUP BY "c"."id"
len(arg)

返回集合中的对象数量。只能在查询中使用,类似于 count()

参数

arg (generator) – 集合

返回类型

numeric

Customer.select(lambda c: len(c.orders) > 2)
max(gen)

返回数据库中的最大值。查询应返回单个属性。

参数

gen (generator) – Python 生成器表达式。

max(o.date_shipped for o in Order)

可以使用 max() 方法生成等效的查询。

min(*args, **kwargs)

返回数据库中的最小值。查询应返回单个属性。

参数

gen (generator) – Python 生成器表达式。

min(p.price for p in Product)

可以使用 min() 方法生成等效的查询。

random()

返回 0 到 1 之间的随机值。此函数在查询中遇到时将被转换为 RANDOM SQL 查询。

示例

select(s.gpa for s in Student if s.gpa > random() * 5)
SELECT DISTINCT "s"."gpa"
FROM "student" "s"
WHERE "s"."gpa" > (random() * 5)
raw_sql(sql, result_type=None)

此函数封装以原始 SQL 格式表达的查询的一部分。如果指定了 result_type,Pony 会将原始 SQL 片段的结果转换为指定的格式。

参数
  • sql (str) – SQL 语句文本。

  • result_type (type) – SQL 语句结果的类型。

>>> q = Person.select(lambda x: raw_sql('abs("x"."age")') > 25)
>>> print(q.get_sql())
SELECT "x"."id", "x"."name", "x"."age", "x"."dob"
FROM "Person" "x"
WHERE abs("x"."age") > 25
x = 10
y = 15
select(p for p in Person if raw_sql('p.age > $(x + y)'))

names = select(raw_sql('UPPER(p.name)') for p in Person)[:]
print(names)

['JOHN', 'MIKE', 'MARY']

查看更多示例 此处

rollback()

回滚当前事务。

此顶层 rollback() 函数调用当前事务中使用的每个数据库对象的 rollback() 方法。

select(gen)

将生成器表达式转换为 SQL 查询,并返回 Query 类的实例。

参数
  • gen (generator) – Python 生成器表达式。

  • globals (dict) –

  • locals (dict) – 可选参数,其中可以包含包含变量及其值的字典,在查询中使用。

返回类型

Query 或列表

您可以迭代结果

for p in select(p for p in Product):
    print p.name, p.price

如果您需要获取对象列表,您可以获取结果的完整切片

prod_list = select(p for p in Product)[:]

select() 函数还可以返回单个属性列表或元组列表

select(p.name for p in Product)

select((p1, p2) for p1 in Product
                for p2 in Product if p1.name == p2.name and p1 != p2)

select((p.name, count(p.orders)) for p in Product)

您可以对结果应用任何 Query 方法,例如 order_by()count()

如果您想对关系属性运行查询,可以使用关系属性的 select() 方法。

show()

在交互模式下打印实体定义或实体实例的属性值。

参数

value – 实体类或实体实例

>>> show(Person)
class Person(Entity):
    id = PrimaryKey(int, auto=True)
    name = Required(str)
    age = Required(int)
    cars = Set(Car)


>>> show(mary)
instance of Person
id|name|age
--+----+---
2 |Mary|22
set_sql_debug(value=True, show_values=None)

将发送到数据库的 SQL 语句打印到控制台或日志文件中。之前的名称 sql_debug 已被弃用。

参数
  • value (bool) – 设置调试开关

  • show_values (bool) – 当 True 时,除了 SQL 文本外,还会记录查询参数 (0.7.3 版本新增)

在 0.7.3 版本之前,它是一个全局标志。现在,在多线程应用程序中,应该为每个线程单独设置它。

默认情况下,Pony 将调试信息发送到标准输出。如果您已配置 标准 Python 日志记录,Pony 将使用它。以下是如何将调试信息存储在文件中

import logging
logging.basicConfig(filename='pony.log', level=logging.INFO)

请注意,我们必须指定 level=logging.INFO,因为默认的标准日志记录级别是 WARNING,而 Pony 默认情况下使用 INFO 级别来记录其消息。Pony 使用两个记录器:pony.orm.sql 用于它发送到数据库的 SQL 语句,以及 pony.orm 用于所有其他消息。

sql_debugging(value=True, show_values=None)

上下文管理器,用于在代码的特定部分启用/禁用记录 SQL 查询。如果您需要为整个 db_session 打开调试,请使用 db_session() 装饰器或上下文管理器的类似参数。

参数
  • value (bool) – 设置调试开关

  • show_values (bool) – 当 True 时,除了 SQL 文本外,还会记录查询参数 (0.7.3 版本新增)

with sql_debugging:  # turn debug on for a specific query
    result = Person.select()

with sql_debugging(show_values=True):  # log with query params
    result = Person.select()

with sql_debugging(False):  # turn debug off for a specific query
    result = Person.select()
sum(gen, distinct=None)

返回从数据库中选取的所有值的总和。

参数
  • gen (generator) – Python 生成器表达式

  • distinct (bool) – distinct 选项

返回类型

numeric

返回值

一个数字。如果查询没有返回任何项,则 sum() 方法返回 0。

sum(o.total_price for o in Order)

可以使用 sum() 方法生成等效的查询。

make_proxy(obj)

为给定对象创建一个代理对象。我们称可以在不同会话中使用的对象为代理对象。这对于非基于请求的应用程序(例如 GUI 独立应用程序)可能很有用。

参数

obj (entity) – 实体对象

返回类型

EntityProxy

返回值

代理对象。

with db_session:
    user = User[id]
    current_user = make_proxy(user)

...

with db_session:
    print(current_user.name)

查询对象

生成器表达式和 lambda 查询返回 Query 类的实例。以下是您可以应用于它的方法列表。

class Query
[start:end]
[index]

限制从数据库中选取的实例数量。在下面的示例中,我们选取了前十个实例

# generator expression query
select(c for c in Customer)[:10]

# lambda function query
Customer.select()[:10]

生成以下 SQL

SELECT "c"."id", "c"."email", "c"."password", "c"."name", "c"."country", "c"."address"
FROM "Customer" "c"
LIMIT 10

如果我们需要选取带有偏移量的实例,我们应该使用 startend

select(c for c in Customer).order_by(Customer.name)[20:30]

它生成以下 SQL

SELECT "c"."id", "c"."email", "c"."password", "c"."name", "c"."country", "c"."address"
FROM "Customer" "c"
ORDER BY "c"."name"
LIMIT 10 OFFSET 20

您也可以使用 limit()page() 方法来实现相同目的。

__len__()

返回从数据库中选取的对象数量。

len(select(c for c in Customer))
avg(distinct=None)

返回所有选取属性的平均值

select(o.total_price for o in Order).avg()

函数 avg() 执行相同操作。

count()

返回与查询条件匹配的对象数量

select(c for c in Customer if len(c.orders) > 2).count()

函数 count() 执行相同操作。

delete(bulk=None)

删除由查询选取的实例。当 bulk=False 时,Pony 将每个实例加载到内存中,并对每个实例调用 Entity.delete() 方法(如果定义了,则调用 before_delete()after_delete() 钩子)。如果 bulk=True,Pony 不会加载实例,它只生成 SQL DELETE 语句,该语句会删除数据库中的对象。

注意

小心使用批量删除

distinct()

在查询中强制使用 DISTINCT

select(c.name for c in Customer).distinct()

但通常情况下,这没有必要,因为 Pony 会以智能的方式自动添加 DISTINCT 关键字。有关此方面的更多信息,请参阅 自动 DISTINCT 部分。

函数 distinct() 执行相同操作。

exists()

如果存在至少一个具有指定条件的实例,则返回 True,否则返回 False

select(c for c in Customer if len(c.cart_items) > 10).exists()

此查询生成以下 SQL

SELECT "c"."id"
FROM "Customer" "c"
  LEFT JOIN "CartItem" "cartitem-1"
    ON "c"."id" = "cartitem-1"."customer"
GROUP BY "c"."id"
HAVING COUNT(DISTINCT "cartitem-1"."id") > 20
LIMIT 1
filter(lambda, globals=None, locals=None)
filter(str, globals=None, locals=None)
filter(**kwargs)

过滤查询结果。传递给 filter() 方法的参数中的条件将被转换为生成的 SQL 查询的 WHERE 部分。 filter() 方法的结果是一个新的查询对象,其中包含指定的附加条件。

注意

此方法类似于 where() 方法。区别在于 filter() 条件应用于从先前查询返回的项,而 where() 条件应用于原始生成器表达式中的循环变量。示例

q = select(o.customer for o in Order)

# c refers to o.customer
q2 = q.filter(lambda c: c.name.startswith('John'))

# o refers to Order object
q3 = q2.where(lambda o: o.total_price > 1000)

filter() 中 lambda 函数参数的名称可以是任意的,但在 where() 中,lambda 参数的名称应与循环变量的名称完全匹配。

注意

where() 方法是在 0.7.3 版本中添加的。在此之前,可以通过在 lambda 函数中未指定参数的情况下使用 filter() 方法来实现相同的功能

q = select(o.customer for o in Order)

# using new where() method
q2a = q.where(lambda o: o.customer.name == 'John Smith')

# old way to do the same using filter() method
q2b = q.filter(lambda: o.customer.name == 'John Smith')

但是这种旧方法有一个缺点:IDE 和 linter 不理解代码,并会警告“未定义的全局变量 o”。使用 where() 就不再是这种情况。在下一个版本中,在 filter() 中使用没有参数的 lambda 函数将被弃用。

使用 lambda 函数指定 filter() 条件

通常,filter() 方法的参数是一个 lambda 函数。lambda 函数的参数表示查询的结果。您可以使用任意名称来表示此参数

q = select(p.name for p in Product)
q2 = q.filter(lambda x: x.startswith('Apple iPad'))

在上面的示例中,x 参数对应于查询 p.name 的结果。这样您就无法在 filter 方法中访问 p 变量,只能访问 p.name。当您需要访问原始查询循环变量时,可以使用 where() 方法。

如果查询返回一个元组,则 filter() lambda 函数参数的数量应与查询结果相对应

q = select((p.name, p.price) for p in Product)
q2 = q.filter(lambda n, p: n.startswith('Apple iPad') and p < 500)

使用关键字参数指定 filter() 条件

过滤查询结果的另一种方法是将参数以命名参数的形式传递

q = select(o.customer for o in Order if o.total_price > 1000)
q2 = q.filter(name="John Smith", country="UK")

关键字参数仅在查询结果为对象时才能使用。在上面的示例中,它是一个 Customer 类型的对象。

filter() 条件指定为文本字符串

此外,filter() 方法可以接收 lambda 函数的文本定义。当您将条件从文本片段组合起来时,可以使用它

q = select(p for p in Product)
x = 100
q2 = q.filter("lambda p: p.price > x")

在上面的示例中,lambda 中的 x 变量指的是之前定义的 x。更安全的解决方案是将字典(其值为 filter() 方法的第二个参数)指定为字典。

q = select(p for p in Product)
q2 = q.filter("lambda p: p.price > x", {"x": 100})
first()

返回选定结果中的第一个元素,如果未找到任何对象,则返回 None

select(p for p in Product if p.price > 100).first()
for_update(nowait=False, skip_locked=False)

注意

nowaitskip_locked参数是互斥的。

参数
  • nowait (bool) – 阻止操作等待其他事务提交。如果选定的行无法立即锁定,则操作会报告错误,而不是等待。

  • skip_locked (bool) – 在 FOR UPDATE 子句中添加 SKIP LOCKED 选项

有时需要锁定数据库中的对象,以防止其他事务同时修改相同的实例。在数据库中,应使用 SELECT FOR UPDATE 查询来执行此锁定。为了使用 Pony 生成此类锁定,您可以调用 for_update 方法

select(p for p in Product if p.picture is None).for_update()

此查询选择所有没有图片的 Product 实例,并锁定数据库中相应的行。锁定将在当前事务提交或回滚时释放。

get()

从数据库中提取一个实体实例。如果存在具有指定参数的对象,则该函数返回该对象;如果不存在此类对象,则返回 None。如果存在多个具有指定参数的对象,则会引发 MultipleObjectsFoundError: Multiple objects were found. Use select(...) to retrieve them 异常。示例

select(o for o in Order if o.id == 123).get()

函数 get() 执行相同的操作。

get_sql()

将 SQL 语句作为字符串返回

sql = select(c for c in Category if c.name.startswith('a')).get_sql()
print(sql)
SELECT "c"."id", "c"."name"
FROM "category" "c"
WHERE "c"."name" LIKE 'a%%'
group_concat(sep=',', distinct=False)

(版本 0.7.4 中新增)

返回一个字符串,该字符串是给定列中所有非 NULL 值的串联。

函数 group_concat() 执行相同的操作。

select(article.tag for article in Article).group_concat(sep=', #')

注意

在 SQLite 中,您不能同时使用 group_concat() 的 sep 和 distinct 参数。

limit(limit=None, offset=None)

限制从数据库中选择的实例数量。

select(c for c in Customer).limit(10, offset=30)

您还可以使用 [start:end]()page() 方法来实现相同目的。

(自版本 0.7.6 起,limit 可以为 None)

max()

返回数据库中的最大值。查询应返回单个属性

select(o.date_shipped for o in Order).max()

函数 max() 执行相同的操作。

min()

返回数据库中的最小值。查询应返回单个属性

select(p.price for p in Product).min()

函数 min() 执行相同的操作。

order_by(attr1[, attr2, ...])
order_by(pos1[, pos2, ...])
order_by(lambda[, globals[, locals]])
order_by(str[, globals[, locals]])

注意

order_by() 的行为将在下一个版本 (0.8) 中发生变化。之前的行为由方法 sort_by() 支持,该方法在版本 0.7.3 中引入。为了与版本 0.8 完全向前兼容,您可以将所有 order_by() 调用替换为 sort_by() 调用。

对查询结果进行排序。目前,order_by()sort_by() 方法以相同的方式工作 - 它们应用于先前查询的结果。

q = select(o.customer for o in Order)

# The following five queries are all equivalent

# Before the 0.8 release
q1 = q.order_by(lambda c: c.name)
q2 = q.order_by(Customer.name)

# Starting from the 0.7.3 release
q3 = q.sort_by(lambda c: c.name)
q4 = q.sort_by(Customer.name)

# After the 0.8 release
q5 = q.order_by(lambda o: o.customer.name)

大多数情况下,查询返回与其迭代相同的对象。在这种情况下,order_by() 的行为在 0.8 版本发布之前和之后将保持不变

# the query returns the loop variable
q = select(c for c in Customer if c.age > 18)

# the next line will work the same way
# before and after the 0.8 release
q2 = q.order_by(lambda c: c.name)

调用 order_by() 方法有几种方法

使用实体属性

select(o for o in Order).order_by(Order.date_created)

要按降序排序,请使用函数 desc()

select(o for o in Order).order_by(desc(Order.date_created))

使用查询结果变量的位置

select((o.customer.name, o.total_price) for o in Order).order_by(-2, 1)

位置编号从 1 开始。减号表示降序排序。在本例中,我们按总价格降序排序,按客户姓名升序排序。

使用 lambda

select(o for o in Order).order_by(lambda o: o.customer.name)

如果 lambda 有参数(在本例中为 o),则 o 代表 select 的结果,并将应用于它。从 0.8 版本开始,它将代表原始查询中的迭代器循环变量。如果您想继续使用查询结果进行排序,则需要使用 sort_by 方法。

使用无参数的 lambda

如果您指定了无参数的 lambda,那么在 lambda 内部,您可以访问查询中定义的所有名称

select(o.total_price for o in Order).order_by(lambda: o.customer.id)

看起来 o 是一个全局变量,但 Pony 将其理解为来自生成器表达式的循环变量名 o。这种行为会混淆 IDE 和 linetrs,它们会警告“访问未定义的全局变量 o”。从 0.8 版本开始,这种使用 order_by() 的方式将不再需要:只需将 o 参数添加到 lambda 函数中即可。

指定字符串表达式

这种方法类似于前一种方法,但您将 lambda 的主体指定为字符串

select(o for o in Order).order_by("o.customer.name")
sort_by(attr1[, attr2, ...])
sort_by(pos1[, pos2, ...])
sort_by(lambda[, globals[, locals]])
sort_by(str[, globals[, locals]])

0.7.3 中新增

对查询结果进行排序。 sort_by() 方法调用中的表达式适用于查询结果中的项目。在 0.8 版本之前,它的工作方式与 order_by() 相同,然后 order_by() 的行为将发生改变。

有多种方法可以调用 sort_by() 方法

使用实体属性

select(o.customer for o in Order).sort_by(Customer.name)

要按降序排序,请使用函数 desc()

select(o.customer for o in Order).sort_by(desc(Customer.name))

使用查询结果变量的位置

select((o.customer.name, o.total_price) for o in Order).sort_by(-2, 1)

位置编号从 1 开始。减号表示降序排序。在本例中,我们按总价格降序排序,按客户姓名升序排序。

使用 lambda

select(o.customer for o in Order).sort_by(lambda c: c.name)

sort_by 方法内部的 lambda 接收先前查询的结果。

指定字符串表达式

这种方法类似于前一种方法,但您将 lambda 的主体指定为字符串

select(o for o in Order).sort_by("o.customer.name")
page(pagenum, pagesize=10)

当您需要将查询结果显示为多个页面时,使用分页。页面编号从页面 1 开始。此方法返回一个切片 [start:end],其中 start = (pagenum - 1) * pagesizeend = pagenum * pagesize

prefetch(*args)

允许指定应与查询结果一起从数据库中加载的哪些相关对象或属性。

通常不需要预取相关对象。当您在 @db_session 中使用查询结果时,Pony 会在您需要时获取所有相关对象。Pony 使用最有效的方式从数据库中加载相关对象,避免 N+1 查询问题。

因此,如果您使用 Flask,建议的方法是在顶层使用 @db_session 装饰器,与您放置 Flask 的 app.route 装饰器的位置相同

@app.route('/index')
@db_session
def index():
    ...
    objects = select(...)
    ...
    return render_template('template.html', objects=objects)

或者,更棒的是,使用 db_session() 装饰器包装 wsgi 应用程序

app.wsgi_app = db_session(app.wsgi_app)

如果由于某种原因您需要将选定的实例与相关对象一起传递到 db_session() 之外,那么您可以使用此方法。否则,如果您尝试在 db_session() 之外访问相关对象,您可能会收到 DatabaseSessionIsOver 异常,例如 DatabaseSessionIsOver: Cannot load attribute Customer[3].name: the database session is over

有关使用 db_session() 的更多信息,请参阅 此处

您可以将实体和/或属性指定为参数。当您指定实体时,将预取所有对应相关对象的“一对一”和非延迟属性。实体的“多对多”属性仅在显式指定时才会预取。

如果您指定属性,则仅预取此特定属性。您可以指定属性链,例如 order.customer.address。预取递归地工作 - 它将指定的参数应用于每个选定对象。

示例

from pony.orm.examples.presentation import *

仅加载 Student 对象,不进行预取

students = select(s for s in Student)[:]

加载学生以及组和部门

students = select(s for s in Student).prefetch(Group, Department)[:]

for s in students: # no additional query to the DB will be sent
    print s.name, s.group.major, s.group.dept.name

与上面相同,但指定属性而不是实体

students = select(s for s in Student).prefetch(Student.group, Group.dept)[:]

for s in students: # no additional query to the DB will be sent
    print s.name, s.group.major, s.group.dept.name

加载学生和相关课程(“多对多”关系)

students = select(s for s in Student).prefetch(Student.courses)

for s in students:
    print s.name
    for c in s.courses: # no additional query to the DB will be sent
        print c.name
random(limit)

从数据库中选择 limit 个随机对象。此方法将使用 ORDER BY RANDOM() SQL 表达式进行转换。实体类方法 select_random() 提供更好的性能,尽管不允许指定查询条件。

例如,选择十个年龄大于 20 岁的随机人员

select(p for p in Person if p.age > 20).random()[:10]
show(width=None)

将查询结果打印到控制台。结果以表格形式格式化。此方法不显示“多对多”属性,因为它需要对数据库进行额外的查询,并且可能很庞大。但是,如果实例具有“一对一”关系,那么它将被显示。

>>> select(p for p in Person).order_by(Person.name)[:2].show()

SELECT "p"."id", "p"."name", "p"."age"
FROM "Person" "p"
ORDER BY "p"."name"
LIMIT 2

id|name|age
--+----+---
3 |Bob |30

>>> Car.select().show()
id|make  |model   |owner
--+------+--------+---------
1 |Toyota|Prius   |Person[2]
2 |Ford  |Explorer|Person[3]
sum()

返回所有选定项目的总和。只能应用于返回单个数字表达式的查询。

select(o.total_price for o in Order).sum()

如果查询不返回任何项目,则查询结果将为 0。

where(lambda, globals=None, locals=None)
where(str, globals=None, locals=None)
where(**kwargs)

版本 0.7.3 中新增

过滤查询结果。传递给 where() 方法的参数条件将被转换为生成的 SQL 查询的 WHERE 部分。 where() 方法的结果是一个新的查询对象,其中包含指定的附加条件。

注意

此方法类似于 filter() 方法。区别在于 filter() 条件适用于从先前查询返回的项目,而 where() 条件适用于来自原始生成器表达式的循环变量。示例

q = select(o.customer for o in Order)

# c refers to o.customer
q2 = q.filter(lambda c: c.name.startswith('John'))

# o refers to Order object
q3 = q2.where(lambda o: o.total_price > 1000)

filter() 中 lambda 函数参数的名称可以是任意的,但在 where() 中,lambda 参数的名称应与循环变量的名称完全匹配。

注意

在添加 where() 方法之前,可以通过使用 filter() 方法来实现相同的功能,其中 lambda 函数的参数未指定

q = select(o.customer for o in Order)

# using new where() method
q2a = q.where(lambda o: o.customer.name == 'John Smith')

# old way to do the same using filter() method
q2b = q.filter(lambda: o.customer.name == 'John Smith')

但是这种旧方法有一个缺点:IDE 和 linter 不理解代码,并会警告“未定义的全局变量 o”。使用 where() 就不再是这种情况。在下一个版本中,在 filter() 中使用没有参数的 lambda 函数将被弃用。

指定 where() 条件使用 lambda 函数

通常,where() 方法的参数是一个 lambda 函数。lambda 函数的参数引用查询循环变量,并且应该具有相同的名称。

q = select(p.name for p in Product).where(lambda p: p.price > 1000)

在上面的示例中,p 参数对应于查询的 p 变量。

where() 方法中,lambda 参数可以引用原始查询中的所有循环变量。

q = select(c.name for c in Customer for o in c.orders)
q2 = q.where(lambda c, o: c.country == 'US' and o.state == 'DELIVERED')

当使用实体的 select() 方法编写查询时,查询没有任何明确定义的循环变量。在这种情况下,lambda 函数的参数应该是实体名称的首字母小写。

q = Product.select()
q2 = q.where(lambda p: p.name.startswith('A'))

指定 where() 条件使用关键字参数

过滤查询结果的另一种方法是将参数以命名参数的形式传递

q = select(o.customer for o in Order if o.total_price > 1000)
q2 = q.where(state == 'DELIVERED')

state 关键字属性引用 Order 对象的 state 属性。

指定 where() 条件作为文本字符串

此外,where() 方法可以接收表达式文本而不是 lambda 函数。当您将条件从文本片段组合在一起时,可以使用它。

q = select(p for p in Product)
x = 100
q2 = q.where("p.price > x")

更安全的解决方案是指定一个字典,其中值作为 where() 方法的第二个参数。

q = select(p for p in Product)
q2 = q.where("p.price > x", {"x": 100})
without_distinct()

默认情况下,Pony 尝试避免查询结果中的重复项,并在认为必要时在查询中智能地添加 DISTINCT SQL 关键字。如果您不希望 Pony 添加 DISTINCT 并获取可能的重复项,可以使用此方法。此方法返回一个新的 Query 对象实例,因此您可以将其与其他查询方法链接起来。

select(p.name for p in Person).without_distinct().order_by(Person.name)

在 Pony Release 0.6 之前,without_distinct() 方法返回查询结果,而不是新的查询实例。

统计信息 - QueryStat

Database 对象具有一个线程本地属性 local_stats,其中包含查询执行统计信息。属性值是一个字典,其中键是 SQL 查询,值是 QueryStat 类的实例。一个 QueryStat 对象具有以下属性。

class QueryStat
sql

SQL 查询的文本。

db_count

将此查询发送到数据库的次数。

cache_count

db_session() 缓存中直接获取查询结果的次数(对于在同一个 db_session() 中重复调用查询的情况)。

min_time

数据库执行查询所需的最小时间。

max_time

数据库执行查询所需的最大时间。

avg_time

数据库执行查询所需的平均时间。

sum_time

花费的总时间(等于 avg_time * db_count)。

Pony 为每个线程分别保留所有统计信息。如果您想查看所有线程的聚合统计信息,则需要调用 merge_local_stats() 方法。另请参阅:local_stats()global_stats()

示例

query_stats = sorted(db.local_stats.values(),
        reverse=True, key=attrgetter('sum_time'))
for qs in query_stats:
    print(qs.sum_time, qs.db_count, qs.sql)