如何在Python中结合aiomysql.create_pool与SQLAlchemy?遇类型错误
问题解决:aiomysql cursor执行SQLAlchemy Select对象报错
错误原因
aiomysql的普通游标(通过conn.cursor()获取)仅支持执行原生SQL字符串,而你传入的user.select()是SQLAlchemy构建的Select查询对象,游标内部尝试计算该对象长度时,因为它不是字符串类型,所以抛出TypeError: object of type 'Select' has no len()。
解决方法
方法一:使用aiomysql的SQLAlchemy集成(推荐)
这是最适配SQLAlchemy的方式,直接用你注释掉的aiomysql.sa.create_engine来创建引擎,它会自动处理SQLAlchemy查询对象到原生SQL的转换:
修改上下文管理代码
async def pg_context(app): conf = app['config']['mysql'] # 恢复使用aiomysql.sa的create_engine engine = await aiomysql.sa.create_engine( db=conf['database'], user=conf['username'], password=conf['password'], host=conf['host'], port=conf['port'], ) app['db'] = engine yield app['db'].close() await app['db'].wait_closed()
修改请求处理代码
sa引擎的连接支持直接执行SQLAlchemy查询对象,无需手动转换:
async def index(request): async with request.app['db'].acquire() as conn: # 直接执行Select对象,返回结果集 result = await conn.execute(user.select()) # 提取所有记录为字典列表 questions = [dict(row) for row in result] return web.Response(text=str(questions))
方法二:手动转换Select对象为原生SQL(适合坚持用create_pool的场景)
如果必须使用普通的create_pool,需要将SQLAlchemy的Select对象编译为原生SQL字符串,并提取参数——绝对不要直接用str()拼接参数,避免SQL注入风险:
async def index(request): async with request.app['db'].acquire() as conn: cursor = await conn.cursor() # 编译Select对象,生成带占位符的SQL和对应参数 select_stmt = user.select() compiled = select_stmt.compile( compile_kwargs={"literal_binds": False}, dialect=conn.dialect ) # 执行带参数的SQL await cursor.execute(str(compiled), compiled.params) records = await cursor.fetchall() # 将记录转为字典(通过cursor.description获取字段名) columns = [desc[0] for desc in cursor.description] questions = [dict(zip(columns, row)) for row in records] return web.Response(text=str(questions))
注意:compile_kwargs={"literal_binds": False}会保留安全的占位符(如%s),配合compiled.params传入参数,这是生产环境的标准做法。若设置为True会直接把参数拼入SQL,仅用于调试,禁止在生产环境使用。
内容的提问来源于stack exchange,提问作者ksks14
相关产品推荐
相关产品推荐

