8

这是故事。

我有一堆存储过程,并且都有自己的参数类型。
我要做的是在 python 中创建一个类型安全层,这样我就可以在访问数据库之前确保所有值的类型都是正确的。

当然,我不想在 python 中再次编写整个模式,所以我想我可以通过从数据库中获取参数名称和类型在启动时自动生成这些信息。

所以我继续破解这个查询只是为了测试

SELECT proname, proargnames, proargtypes 
FROM pg_catalog.pg_namespace n
JOIN pg_catalog.pg_proc p
ON pronamespace = n.oid
WHERE nspname = 'public';

然后我从 python 运行它,对于'proargtypes',我为每个结果得到一个这样的字符串

'1043 23 1043'

我敏锐的眼睛告诉我这些是 postgresql 类型的 oid,用空格分隔,这个特殊的字符串意味着函数接受 varchar、integer、varchar。所以用python说话,这应该是

(unicode, int, unicode)

现在如何从这些数字中获取 python 类型?

理想的最终结果是这样的

In [129]: get_python_type(23)
Out[129]: int

我查看了 psycopg2 的所有内容,发现最接近的是“extensions.string_types”,但这只是将 oids 映射到 sql 类型名称。

4

2 回答 2

6

如果您想要 Python 类型类,就像您可能从SQLALchemy列对象中获得的那样,您将需要构建和维护自己的映射。psycopg2没有之一,即使在内部。

但是,如果您想要的是一种oid将原始值转换为 Python 实例的函数的方法,psycopg2.extensions.string_types那么实际上已经是您所需要的了。它可能看起来只是从oid名称到名称的映射,但事实并非如此:它的值不是字符串,它们是psycopg2._psycopg.type. 是时候深入研究一些代码了。

psycopg2公开一个用于注册新类型转换器的 API,我们可以使用它来追溯涉及类型转换的 C 代码;这以typecastObjectin typecast.c为中心,不出所料,它映射到psycopg2._psycopg.type我们在 oldfriend 中找到的string_types。这个对象包含指向两个函数的指针,pcast(对于 Python 转换函数)和ccast(对于 C 转换函数),这似乎是我们想要的——只要选择存在的一个并调用它,问题就解决了。除了它们不在暴露的属性中(name,它只是一个标签,并且values,它是一个 oid 列表)。类型确实暴露给 Python 的是__call__,事实证明,它只是在 和 之间进行pcast选择ccast为我们。此方法的文档非常无用,但查看 C 代码进一步表明它需要两个参数:一个包含原始值的字符串和一个游标对象。

>>> import psycopg2.extensions
>>> cur = something_that_gets_a_cursor_object()
>>> psycopg2.extensions.string_types[23]
<psycopg2._psycopg.type 'INTEGER' at 0xDEADBEEF>
>>> psycopg2.extensions.string_types[23]('100', cur)
100
>>> psycopg2.extensions.string_types[23]('10.0', cur)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '10.0'
>>> string_types[1114]
<psycopg2._psycopg.type 'DATETIME' at 0xDEADBEEF>
>>> string_types[1114]('2018-11-15 21:35:21', cur)
datetime.datetime(2018, 11, 15, 21, 35, 21)

不幸的是,需要一个游标,事实上,并不总是需要一个游标:

>>> string_types[23]('100', None)
100

但是任何必须转换实际字符串(varchar例如)类型的事情都依赖于 PostgreSQL 服务器的语言环境,至少在 Python 3 编译中,并且传递None给这些施法者不仅会失败 - 它还会出现段错误。您提到的方法cursor.cast(oid, raw)本质上是对脚轮的包装,psycopg2.extensions.string_types在某些情况下可能更方便。

我能想到的需要光标和连接的唯一解决方法是构建一个模拟连接对象。如果它在未连接到实际数据库的情况下公开了所有相关的环境信息,则可以将其附加到游标对象并使用 withstring_types[oid](raw, cur)或 with cur.cast(oid, raw),但该模拟将使用 C 语言构建,并留给读者作为练习。

于 2018-11-15T20:49:26.627 回答
2

postgres 类型和 python 类型的映射在这里给出。这有帮助吗?

编辑:当您从表中读取记录时,postgres(或任何数据库)驱动程序将自动将记录列类型映射到 Python 类型。

cur = con.cursor()
cur.execute("SELECT * FROM Writers")

row = cur.fetchone()

for index, val in enumerate(row):
    print "column {0} value {1} of type {2}".format(index, val, type(val))

现在,您只需在编写 MySQL 接口代码时将 Python 类型映射到 MySQL 类型。但是,坦率地说,这是将类型从 PostgreSQL 类型映射到 MySQL 类型的一种迂回方式。我只想参考这两个数据库之间的众多类型映射之一

于 2012-06-29T07:04:02.460 回答