You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SQLAlchemy查询中多UUID列类型转换问题求助

解决MariaDB+mysqlconnector下SQLAlchemy读取UUID列的TypeError问题

问题描述

从UUID类型的列中获取值时,调用CursorObject的fetchall()方法会触发Python错误:TypeError("a bytes-like object is required, not 'str'")。

报错代码(单列查询场景)

import sqlalchemy as sa

def getScheduleData(engine: sa.engine, scheduleID: str) -> dict:
    rows = []
    metadata = sa.MetaData()
    table = sa.Table('schedules', metadata, autoload_with=engine)

    stmt = sa.select(table.c['userID']).where(table.c["ID"] == scheduleID)
    print(stmt)

    try:
        with engine.connect() as conn:
            result = conn.execute(stmt)
            rows = result.fetchall()
            print(rows)
    except Exception as e:
        print(e)

更复杂场景:查询整行数据(多UUID列)

当schedules表包含多个UUID类型列,查询整行时同样触发上述错误:

import sqlalchemy as sa

def getScheduleData(engine: sa.engine, scheduleID: str) -> dict:
    rows = []
    metadata = sa.MetaData()
    table = sa.Table('schedules', metadata, autoload_with=engine)

    # 查询所有列
    stmt = sa.select(table).where(table.c["ID"] == scheduleID)
    print(stmt)

    try:
        with engine.connect() as conn:
            result = conn.execute(stmt)
            rows = result.fetchall()
            print(rows)
    except Exception as e:
        print(e)

已有的单列解决方案

针对单个UUID列,可以通过sa.cast()将其转为字符串避免报错,但仅适用于指定列:

import sqlalchemy as sa

def getScheduleData(engine: sa.engine, scheduleID: str) -> dict:
    rows = []
    metadata = sa.MetaData()
    table = sa.Table('schedules', metadata, autoload_with=engine)

    # 将列值转为字符串
    stmt = sa.select(sa.cast(table.c['userID'], sa.String(36))).where(table.c["ID"] == scheduleID)
    print(stmt)

    try:
        with engine.connect() as conn:
            result = conn.execute(stmt)
            rows = result.fetchall()
            print(rows)
    except Exception as e:
        print(e)

当前核心问题:如何在查询整行数据且存在多个UUID类型列的场景下,批量实现类似的类型转换?

环境补充信息

  • 驱动:mysqlconnector
  • 数据库:MariaDB
  • 引擎创建代码:
self._engine_str = f"mysql+mysqlconnector://{self._user}:{self._password}@{self._host}/"
engine = sa.create_engine(f"{self._engine_str}{db_name}")

错误栈

Traceback (most recent call last):
  File "C:\projects\myWebApp\frontend\fastapi\controllers\schedule.py", line 598, in getScheduleData
    rows = result.fetchall()
           ^^^^^^^^^^^^^^^^^
  File "c:\Users\Luke\.conda\envs\mywebapp-server\Lib\site-packages\sqlalchemy\engine\result.py", line 1317, in fetchall
    return self._allrows()
           ^^^^^^^^^^^^^^^
  File "c:\Users\Luke\.conda\envs\mywebapp-server\Lib\site-packages\sqlalchemy\engine\result.py", line 551, in _allrows
    made_rows = [make_row(row) for row in rows]
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "c:\Users\Luke\.conda\envs\mywebapp-server\Lib\site-packages\sqlalchemy\engine\result.py", line 551, in <listcomp>
    made_rows = [make_row(row) for row in rows]
                 ^^^^^^^^^^^^^
  File "lib\\sqlalchemy\\cyextension\\resultproxy.pyx", line 22, in sqlalchemy.cyextension.resultproxy.BaseRow.__init__
  File "lib\\sqlalchemy\\cyextension\\resultproxy.pyx", line 79, in sqlalchemy.cyextension.resultproxy._apply_processors
  File "c:\Users\Luke\.conda\envs\mywebapp-server\Lib\site-packages\sqlalchemy\sql\sqltypes.py", line 3615, in process
    value = _python_UUID(value)
            ^^^^^^^^^^^^^^^^^^^
  File "c:\Users\Luke\.conda\envs\mywebapp-server\Lib\uuid.py", line 175, in __init__
    hex = hex.replace('urn:', '').replace('uuid:', '')
          ^^^^^^^^^^^^^^^^^^^^^^^
TypeError: a bytes-like object is required, not 'str'

解决方案

方法1:批量转换所有UUID列为字符串(整行查询场景)

遍历表的所有列,对UUID类型的列自动应用sa.cast()转换,其他列保持原样:

import sqlalchemy as sa
from sqlalchemy.sql.sqltypes import UUID

def getScheduleData(engine: sa.engine, scheduleID: str) -> dict:
    rows = []
    metadata = sa.MetaData()
    table = sa.Table('schedules', metadata, autoload_with=engine)

    # 批量处理列:UUID列转字符串,其他列直接选择
    selected_columns = [
        sa.cast(col, sa.String(36)) if isinstance(col.type, UUID) else col
        for col in table.c
    ]

    stmt = sa.select(*selected_columns).where(table.c["ID"] == scheduleID)
    print(stmt)

    try:
        with engine.connect() as conn:
            result = conn.execute(stmt)
            rows = result.fetchall()
            print(rows)
    except Exception as e:
        print(e)

方法2:自定义UUID类型处理器(全局生效)

通过自定义SQLAlchemy的UUID类型处理器,统一处理从数据库返回的UUID值,避免类型不匹配:

import sqlalchemy as sa
from sqlalchemy import types
import uuid

class StringUUID(types.TypeDecorator):
    impl = types.String
    cache_ok = True

    def process_bind_param(self, value, dialect):
        if value is None:
            return value
        return str(value)

    def process_result_value(self, value, dialect):
        if value is None:
            return value
        # 处理驱动可能返回的bytes类型
        if isinstance(value, bytes):
            value = value.decode('utf-8')
        return uuid.UUID(value)

# 创建引擎时无需额外参数,加载表时指定UUID列使用自定义类型
engine = sa.create_engine(
    f"mysql+mysqlconnector://{self._user}:{self._password}@{self._host}/{db_name}"
)

metadata = sa.MetaData()
table = sa.Table(
    'schedules',
    metadata,
    # 覆盖UUID列的类型定义
    sa.Column('ID', StringUUID),
    sa.Column('userID', StringUUID),
    # 其他列保持自动加载
    autoload_with=engine,
    autoload_replace=False
)

# 后续查询无需额外转换
def getScheduleData(engine: sa.engine, scheduleID: str) -> dict:
    rows = []
    stmt = sa.select(table).where(table.c["ID"] == scheduleID)
    try:
        with engine.connect() as conn:
            result = conn.execute(stmt)
            rows = result.fetchall()
            print(rows)
    except Exception as e:
        print(e)

方法3:调整连接参数(根源解决)

mysqlconnector驱动对UUID的处理存在兼容性问题,可尝试在创建引擎时添加use_unicode=True参数,确保驱动返回字符串而非bytes类型:

engine = sa.create_engine(
    f"mysql+mysqlconnector://{self._user}:{self._password}@{self._host}/{db_name}",
    connect_args={"use_unicode": True}
)

内容的提问来源于stack exchange,提问作者Furrier

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 21:14:57