我试图从使用 asyncpg 执行的 postgresql 查询返回原始数据(如字符串/字节),而无需 asyncpg 将输出数据解析为记录/字典。就我而言,我使用光标,但我认为这个问题很普遍。
为什么?
我正在返回预处理的 json 数据,为端点做好准备。我不想解析这些数据,将其转换为字典,然后在 python 中将其重新编码为 json。看来很多努力都白费了。我返回的数据非常多,因此我希望防止这种情况会导致性能显着提高。
我的代码基本上是这样的:
query = "SELECT ARRAY_AGG(JSON_STRIP_NULLS('my', 'data', 'from', (select array_agg(somewhere) from databasetable)))"
# Never mind the actual query. It is big, but returns many lines of json.
# eg:
# {"my": "data", "from": [1,2,3]}
# {"my": "data", "from": [1,2,4]}
# {"my": "data", "from": [1,2,5]}
async def my_generator():
async with get_transaction_somehow() as conn: # type: asyncpg.Connection | asyncpg.pool.PoolConnectionProxy
async for record in conn.cursor(query, my_data):
yield record # Here I just want to spew out whatever asyncpg gets from postgres
最简单、最通用的解决方案是在查询中将值强制转换为
text
,这样驱动程序就不会尝试反序列化它。
SELECT some_json_value::text FROM tbl
在 asyncpg 的特定情况下,只有设置了解码器,JSON 值才会被反序列化,因此默认情况下 JSON 值将加载为
str
,但是这样的配置可能不方便,因为通常会自动(反)序列化至少在某些时候需要。
这是一个完整的演示脚本:
import asyncio
import json
import asyncpg
async def main():
conn = await asyncpg.connect('postgresql:///test')
await conn.set_type_codec(
'jsonb', encoder=json.dumps, decoder=json.loads, schema='pg_catalog'
)
await conn.execute("""DROP TABLE IF EXISTS t78449946""")
await conn.execute(
"""
CREATE TABLE t78449946 (
c JSONB
)
"""
)
# Insert a record into the created table.
await conn.execute(
"""INSERT INTO t78449946 (c) VALUES($1)""",
{},
)
row = await conn.fetchrow("""SELECT c FROM t78449946""")
assert isinstance(row['c'], dict)
row = await conn.fetchrow("""SELECT c::text FROM t78449946""")
assert isinstance(row['c'], str)
await conn.close()
asyncio.get_event_loop().run_until_complete(main())