将psql类型oids映射到python类型 - 使用psycopg2

时间:2012-06-29 00:40:37

标签: python postgresql psycopg2

这是故事。

我有一堆存储过程,并且都有自己的参数类型 我想要做的是在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',但这只是将oid映射到sql类型名称。

2 个答案:

答案 0 :(得分:1)

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

编辑: 当您从表中读取记录时,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类型的迂回方式。我只想引用这两个数据库之间的众多类型映射之一,如this

答案 1 :(得分:1)

如果要使用Python类型类(例如,可以从SQLALchemy列对象中获取),则需要构建和维护自己的映射。 psycopg2甚至在内部都没有。

但是,如果您想要的是从oid到将原始值转换为Python实例的函数的方法,则psycopg2.extensions.string_types实际上已经是您所需要的。看起来好像只是从oid到名称的映射,但这不是真的:它的值不是字符串,而是psycopg2._psycopg.type的实例。是时候研究一些代码了。

psycopg2提供了用于注册新类型转换器的API,我们可以使用该API追溯到与类型转换有关的C代码;围绕typecastObject中的 typecast.c,毫不奇怪,它映射到我们在老朋友psycopg2._psycopg.type中找到的string_types。该对象包含指向两个函数的指针,pcast(用于Python转换功能)和ccast(用于C转换功能),这看起来像我们想要的那样—只需选择存在的那个并称其为问题解决了。除非它们不在公开的属性(name,这只是一个标签,values,这是一个oid列表)之内。该类型真正暴露给Python的是__call__,事实证明,它只是为我们在pcastccast之间进行选择。该方法的文档完全没有帮助,但是进一步查看C代码shows,它需要两个参数:一个包含原始值的字符串和一个游标对象。

>>> 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类型)所必需的工作至少取决于Python 3编译中的PostgreSQL服务器的语言环境,并且将None传递给那些脚轮并不会”只是失败-它存在段错误。您提到的方法cursor.cast(oid, raw)实际上是psycopg2.extensions.string_types中脚轮的包装,在某些情况下可能更方便。

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