连接到数据库

在开始使用实体之前,您需要创建 Database 对象。您在 Python 代码中声明的实体将通过此对象映射到数据库。

将实体映射到数据库可以分为四个步骤

  • 创建 Database 对象

  • 定义与该 Database 对象相关的实体

  • Database 对象绑定到特定数据库

  • 将实体映射到数据库表

现在我们将描述使用 Database 对象及其方法的主要工作流程。如果您需要更多详细信息,可以在 API 参考 中找到。

创建数据库对象

在此步骤中,我们只需创建一个 Database 类的实例

db = Database()

Database 类实例具有一个属性 Entity,它代表用于实体声明的基类。

将数据库对象绑定到特定数据库

在将实体映射到数据库之前,我们需要连接到它以建立连接。可以使用 bind() 方法完成此操作

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

此方法的第一个参数是数据库提供程序的名称。数据库提供程序是一个位于 pony.orm.dbproviders 包中的模块,它知道如何与特定数据库进行交互。在数据库提供程序名称之后,您应该指定将传递给相应 DBAPI 驱动程序的 connect() 方法的参数。

目前,Pony 可以与以下数据库系统一起使用:SQLite、PostgreSQL、MySQL、Oracle、CockroachDB,相应的 Pony 提供程序名称为:'sqlite''postgres''mysql''oracle''cockroach'。Pony 可以轻松扩展以合并其他数据库提供程序。

当您刚开始使用 Pony 时,可以使用 SQLite 数据库。此数据库包含在 Python 发行版中,您无需单独安装任何内容。使用 SQLite,您可以在文件或内存中创建数据库。要创建文件中的数据库,请使用以下命令

db.bind(provider='sqlite', filename='database.sqlite', create_db=True)

create_db=True 时,如果数据库文件不存在,Pony 将创建它。如果它已经存在,Pony 将使用它。

对于内存数据库,如果您从单个线程工作,请使用此方法

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

…以及此方法,如果您需要从多个线程访问同一个内存数据库

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

创建内存数据库时,无需使用参数 create_db。这是一种在交互式 shell 中使用 Pony 时创建 SQLite 数据库的便捷方法,但您应该记住,整个内存数据库将在程序退出时丢失。

以下是绑定到其他数据库的示例

db.bind(provider='sqlite', filename=':sharedmemory:')
db.bind(provider='sqlite', filename='filename', create_db=True)
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')

您可以在 API 参考中找到有关使用每个数据库的更多详细信息

将实体映射到数据库表

创建 Database 对象、定义实体并绑定数据库后,下一步是使用 generate_mapping() 方法将实体映射到数据库表

db.generate_mapping(create_tables=True)

此方法创建表、外键引用和索引(如果它们不存在)。映射实体后,您可以在 Python 代码中开始使用它们 - 选择、创建、修改对象并将它们保存到数据库中。

数据库对象的方法和属性

Database 对象有一组方法,您可以在 API 参考 中查看。

使用数据库对象进行原始 SQL 查询

通常,您将使用实体并让 Pony 与数据库进行交互,但 Pony 还允许您使用 SQL 与数据库进行交互,甚至将这两种方式结合起来。当然,您可以使用 DBAPI 接口直接与数据库进行交互,但使用 Database 对象可以为您提供以下优势

  • 使用 db_session() 装饰器或上下文管理器进行自动事务管理。在事务完成后,所有数据将存储到数据库中,或者如果发生异常,则回滚。

  • 连接池。无需跟踪数据库连接。您在需要时拥有连接,并在完成事务后,连接将返回到池中。

  • 统一的数据库异常。每个 DBAPI 模块都定义了自己的异常。Pony 允许您在使用任何数据库时使用相同的异常集。这有助于您创建可以从一个数据库移植到另一个数据库的应用程序。

  • 将参数传递给 SQL 查询的统一方式,并防止注入攻击。不同的数据库驱动程序使用不同的参数样式 - DBAPI 规范提供了 5 种不同的将参数传递给 SQL 查询的方式。使用 Database 对象,您可以对所有数据库使用一种传递参数的方式,并消除 SQL 注入的风险。

  • 使用 get()select() 方法时,自动解包单列结果。如果 select() 方法只返回一列,Pony 返回一个值列表,而不是一个元组列表,每个元组只有一个项目,就像 DBAPI 一样。如果 get() 方法返回一列,它只返回一个值,而不是一个包含一个项目的元组。这很方便。

  • 当方法 select()get() 返回多列时,Pony 使用智能元组,允许使用列名而不是元组索引来访问项目作为元组属性。

换句话说,Database 对象可以帮助您节省完成例行任务的时间,并提供便利性和统一性。

在原始 SQL 查询中使用参数

使用 Pony,您可以轻松地将参数传递到 SQL 查询中。要指定参数,您需要在变量名前面加上 $ 符号

x = "John"
data = db.select("select * from Person where name = $x")

当 Pony 在 SQL 查询中遇到这样的参数时,它会从当前帧(从全局变量和局部变量)或从作为第二个参数传递的字典中获取变量值。在上面的示例中,Pony 将尝试从变量 x 中获取 $x 的值,并将此值作为参数传递给 SQL 查询,从而消除了 SQL 注入的风险。下面您可以看到如何传递包含参数的字典

data = db.select("select * from Person where name = $x", {"x" : "Susan"})

这种将参数传递给 SQL 查询的方法非常灵活,不仅允许使用单个变量,还可以使用任何 Python 表达式。为了指定表达式,您需要在 $ 符号后将其括在括号中。

data = db.select("select * from Person where name = $(x.lower()) and age > $(y + 2)")

所有参数都可以使用 Pony 的统一方式传递到查询中,与 DBAPI 提供程序无关,使用 $ 符号。在上面的示例中,我们将 nameage 参数传递到查询中。

可以在查询文本中包含 Python 表达式,例如

x = 10
a = 20
b = 30
db.execute("SELECT * FROM Table1 WHERE column1 = $x and column2 = $(a + b)")

如果您需要在查询中将 $ 符号用作字符串文字,则需要使用另一个 $ 对其进行转义(连续放置两个 $ 符号:$$)。

自定义连接行为

您可以使用 db.on_connect() 装饰器执行一些查询来指定您的连接(即 pragma)。

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)

使用以下代码,每个新的 sqlite 连接都会调用此函数。此示例展示了如何为 sqlite 恢复旧的区分大小写的 like

数据库统计信息

Database 对象会保留执行查询的统计信息。您可以检查哪些查询执行得更频繁,执行它们花费了多长时间,以及其他一些参数。有关更多详细信息,请查看 API 参考中的 QueryStat 类。