使用SqlAlchemy向PostgreSQL TSRANGE列插入值触发报错
问题根因
- 首次报错核心是数据库驱动类型不匹配:你当前使用的异步数据库驱动是asyncpg,但代码里导入的
DateTimeRange是同步驱动psycopg2专属的范围类型实现,asyncpg无法识别该类实例,它仅支持自身实现的Range对象、元组、列表三类合法范围输入。 - 第二次使用
text()拼接的报错由两个问题共同触发:一是你写的时间范围逻辑错误,结束时间2020年早于开始时间2022年,本身就不符合TSRANGE的取值规则;二是直接将text构造的SQL表达式作为字段值传入参数,会被FastAPI的jsonable_encoder当成普通Python对象尝试序列化,而范围类型本身不支持迭代、也没有__dict__属性,直接触发序列化异常。
解决方案
方案1:使用asyncpg原生Range对象(推荐,类型安全)
直接从asyncpg导入Range类构造时间范围,完全适配异步驱动的序列化逻辑:
from asyncpg import Range from datetime import datetime insert_stmt = StaffAvailability.insert() avail_id = await database.execute(insert_stmt, values={ "staff_id": kwargs.get("staff_id"), "during": Range( lower=datetime(2021, 3, 22, 10, 0, 0), upper=datetime(2021, 3, 25, 10, 0, 0), bounds="[)" # 边界规则:[代表包含左端点,)代表不包含右端点,和PostgreSQL TSRANGE默认规则一致,可按需调整 ) })
方案2:传元组自动转换(无需额外导入类型类)
asyncpg支持自动将符合格式的元组转换为PG范围类型,不需要依赖特定Range类:
from datetime import datetime insert_stmt = StaffAvailability.insert() avail_id = await database.execute(insert_stmt, values={ "staff_id": kwargs.get("staff_id"), # 元组格式固定为:(下界值, 上界值, 边界字符串),边界参数可省略,省略时默认使用左闭右开规则[) "during": (datetime(2021, 3, 22, 10, 0, 0), datetime(2021, 3, 25, 10, 0, 0), "[)") })
方案3:SQL语句层面直接转换字符串
如果需要直接传字符串格式的范围值,要把类型转换逻辑写在SQL语句构造层,不要把text对象当成普通值传入:
from sqlalchemy import text insert_stmt = StaffAvailability.insert().values( staff_id=kwargs.get("staff_id"), # 注意修正时间顺序,上界必须晚于下界 during=text("CAST('[2022-10-10 10:00, 2022-10-10 12:00)' AS TSRANGE)") ) avail_id = await database.execute(insert_stmt)
注意:PostgreSQL所有范围类型都要求上界大于等于下界,传入反序的时间会直接触发数据库报错;异步技术栈下不要混用psycopg2和asyncpg的专属数据类型,两个驱动的类型序列化逻辑完全不互通。
内容的提问来源于stack exchange,提问作者Koushik Das
相关产品推荐
相关产品推荐

