Python DB-API:如何处理不同的参数样式?

15 投票
4 回答
6082 浏览
提问于 2025-04-16 04:47

我正在实现一个Python本体类,这个类使用数据库作为后端来存储和查询本体。数据库的结构是固定的(事先指定好),但我不知道具体使用的是哪种数据库引擎。不过,我可以依赖于一个事实,那就是这个数据库引擎的Python接口遵循Python DB-API 2.0(PEP 249)。一个简单的想法是让用户把一个符合PEP 249标准的Connection对象传递给我的本体类的构造函数,然后我会使用一些硬编码的SQL查询来查询数据库:

class Ontology(object):
    def __init__(self, connection):
        self.connection = connection

    def get_term(self, term_id):
        cursor = self.connection.cursor()
        query = "SELECT * FROM term WHERE id = %s"
        cursor.execute(query, (term_id, ))
        [...]

我的问题是,不同的数据库后端可能支持不同的参数标记,这些标记由后端模块的paramstyle属性定义。例如,如果paramstyle = 'qmark',那么接口支持问号样式(SELECT * FROM term WHERE id = ?);paramstyle = 'numeric'表示数字位置样式(SELECT * FROM term WHERE id = :1);paramstyle = 'format'表示ANSI C格式字符串样式(SELECT * FROM term WHERE id = %s)。如果我想让我的类能够处理不同的数据库后端,似乎我必须为所有的参数标记样式做好准备。这似乎违背了我使用通用数据库API的初衷,因为我无法在不同的数据库后端使用相同的参数化查询。

有没有解决这个问题的方法?如果有,最好的方法是什么?数据库API并没有规定存在一个通用的转义函数来清理我查询中的值,所以手动进行转义也不是一个选项。我也不想通过使用更高层次的抽象(比如SQLAlchemy)来给项目增加额外的依赖。

4 个回答

1

让我困惑的地方是,如何判断在你的代码中需要什么样的参数样式,尤其是当你只得到一个连接或游标对象时。以下是我想到的解决办法:

import importlib

def get_paramstyle(conn):
    name = conn.__class__.__module__.split('.')[0]
    mod = importlib.import_module(name)
    return mod.paramstyle

你可能需要对这个连接对象(conn)做更多的检查,或者至少把这段代码放在一个try块里,这要看你愿意做什么样的假设。

7
  • 这个Python示例可能对你有帮助。它引入了一个额外的抽象层,把参数包装在自己的Param类里。

  • PyDal项目可能更接近你想要实现的目标:“PyDal使得可以在任何符合DBAPI 2.0的模块中使用相同的参数样式和日期时间类型。此外,参数样式和日期时间类型是可以配置的。

2

严格来说,这个问题并不是因为数据库API允许这样做,而是因为不同的数据库使用不同的SQL语法。数据库API模块会将确切的查询字符串和参数一起传递给数据库。参数标记的“解析”是由数据库本身完成的,而不是由数据库API模块来处理。

这意味着,如果你想解决这个问题,就需要引入一些更高级的抽象。如果你不想增加额外的依赖项,那你就得自己动手。不过,与其手动转义和替换,不如尝试根据后端模块的参数样式,动态地将查询字符串中的参数标记替换成所需的参数标记。然后将带有参数标记的字符串传递给数据库。例如,你可以在查询中到处使用'%s',然后用Python的字符串替换功能将'%s'替换成':1'、':2'等,如果数据库使用的是'数值'样式,依此类推……

撰写回答