API 参考¶
目录
数据库¶
Database 类¶
- class Database¶
The
Database
对象使用连接池管理数据库连接。它是线程安全的,可以在应用程序的所有线程之间共享。TheDatabase
对象允许使用 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 连接。
- 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 语句文本的只读属性。它可用于调试。
- 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。有关这些驱动程序的更多信息,请参阅 MySQLdb 和 pymysql 文档。
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
注意
IntArray、StrArray 和 FloatArray 类型仅在 PostgreSQL 和 SQLite 中受支持。
您还可以指定另一个实体作为属性类型来定义两个实体之间的关系。
Python 2 和 3 中的字符串¶
如您所知,Python 3 在字符串方面与 Python 2 有所不同。Python 2 提供两种字符串类型 - str
(字节字符串)和 unicode
(unicode 字符串),而在 Python 3 中,str
类型表示 unicode 字符串,而 unicode
则被删除了。
在 0.6 版发布之前,Pony 将 str
和 unicode
属性存储为数据库中的 unicode,但对于 str
属性,它必须在从数据库读取时将 unicode 转换为字节字符串。从 Pony 0.6 版开始,Python 2 中 str
类型的属性的行为就像它们被声明为 unicode
属性一样。现在,无论您指定 str
还是 unicode
作为属性类型,都没有区别 - 您将在 Python 和数据库中拥有 unicode 字符串。
从 Pony 0.6 版开始,添加了对 Python 3 的支持,我们建议使用 str
和 LongStr
类型,而不是 unicode
和 LongUnicode
。 LongStr
和 LongUnicode
在数据库中存储为 CLOB。
LongUnicode
和 LongStr
也是如此。 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
类型。 buffer
和 bytes
类型在数据库中存储为二进制(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)
您也可以使用 precision
和 scale
选项。
class Product(db.Entity):
price = Required(Decimal, precision=10, scale=2) # DECIMAL(10, 2)
如果您没有指定 precision
和 scale
参数,Pony 默认使用 Decimal(precision=12, scale=2)
值。
日期时间、时间和时间差精度¶
datetime
和 time
类型接受一个位置参数,该参数指定列的精度。默认情况下,对于大多数数据库,它等于 6。
对于 MySQL 数据库,默认值为 0。在 MySQL 5.6.4 版本之前,DATETIME
和 TIME
列 根本无法存储小数秒。从 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 始终执行级联删除,即使另一端定义为Optional
。False
表示 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) 适用于
Required
和Optional
关系属性,允许指定数据库中外键的名称。
- index¶
(bool|str) 允许控制此列的索引创建。
index=True
- 将使用默认名称创建索引。index='index_name'
- 使用指定的名称创建索引。index=False
- 跳过索引创建。如果未指定“index”选项,则 Pony 仍会使用默认名称为外键创建索引。
- lazy¶
(bool) 当为
True
时,Pony 会在加载对象时延迟加载属性值。在您尝试直接访问此属性之前,不会加载该值。默认情况下,lazy
为LongStr
和LongUnicode
设置为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) 设置
Decimal
、time
、timedelta
、datetime
属性类型的精度。
- py_check¶
(function) 允许指定一个函数,该函数将用于在将值分配给属性之前检查该值。该函数应返回
True
或False
。它也可以在检查失败时引发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 和 64attr1 = 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 会自动为该属性分配
min
和max
值。例如,大小为 8 的有符号属性将接收min
值 -128 和max
值 127,而大小相同的无符号属性将接收min
值 0 和max
值 255。如果需要,您可以用自己的值覆盖min
和max
,但这些值不应超过大小隐含的范围。从 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 集合,并使用标准操作,如 in
、not in
、len
。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)¶
从集合中删除一个或多个项目,从而断开实体实例之间的关系。
实体选项¶
- _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)¶
注意
nowait和skip_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 结构更有效的算法。该方法使用以下算法从表中确定最大 id。
在 (0, max_id] 范围内生成随机 id。
通过这些随机 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
如果我们需要选取带有偏移量的实例,我们应该使用
start
和end
值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
- __len__()¶
返回从数据库中选取的对象数量。
len(select(c for c in Customer))
- delete(bulk=None)¶
删除由查询选取的实例。当
bulk=False
时,Pony 将每个实例加载到内存中,并对每个实例调用Entity.delete()
方法(如果定义了,则调用before_delete()
和after_delete()
钩子)。如果bulk=True
,Pony 不会加载实例,它只生成 SQL DELETE 语句,该语句会删除数据库中的对象。注意
小心使用批量删除
before_delete()
和after_delete()
钩子不会在已删除的对象上调用。如果对象已加载到内存中,它不会在批量删除时从
db_session()
缓存中删除。
- 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)¶
注意
nowait和skip_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)
- 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) * pagesize
,end = 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)