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

SQLModel中GUID主键查询返回字符串而非UUID类型的问题

SQLModel查询返回GUID为字符串而非UUID类型的问题

我正在使用SQLModel进行开发,定义了以下基表和数据表:

from sqlmodel import SQLModel, Field, Column, text
from uuid import UUID, Optional
from sqlalchemy import UNIQUEIDENTIFIER

class GUIDModel(SQLModel):
    """
    提供以GUID作为主键的表基类
    """

    guid: Optional[UUID] = Field(
        ...,
        primary_key=True,
        description=DescriptionConstants.GUID,
        sa_column=Column(
            "guid",
            UNIQUEIDENTIFIER,
            nullable=False,
            primary_key=True,
            server_default=text("newsequentialid()"),
        ),
    )

class Project(GUIDModel):
    name: str = Field(max_length=255, description=DescriptionConstants.NAME)

一切运行正常,但通过SQLAlchemy查询数据行时,返回的GUID是字符串类型而非UUID类型。

测试代码如下:

def test_get_project(self):
    with Session(__get_engine()) as session:
        project: Project = Projects._get_project(session)
        self.assertEqual(type(project.guid), uuid.UUID)

触发的错误信息:

<class 'uuid.UUID'> != <class 'str'>
Expected :<class 'str'>
Actual   :<class 'uuid.UUID'>
<Click to see difference>

问题原因

SQLAlchemy对SQL Server的UNIQUEIDENTIFIER类型默认处理为返回字符串,不会自动转换为Python的uuid.UUID对象,导致查询结果中guid字段类型与模型定义的UUID类型不符。

解决方法

方法1:自定义SQLAlchemy GUID类型

实现一个兼容多数据库的GUID类型,自动在字符串和UUID对象之间转换:

from sqlalchemy.types import TypeDecorator, CHAR
import uuid
from sqlalchemy import UNIQUEIDENTIFIER

class GUID(TypeDecorator):
    """兼容多数据库的GUID类型,SQL Server使用UNIQUEIDENTIFIER,其他数据库用CHAR(36)"""
    impl = CHAR
    cache_ok = True

    def load_dialect_impl(self, dialect):
        if dialect.name == 'mssql':
            return dialect.type_descriptor(UNIQUEIDENTIFIER())
        else:
            return dialect.type_descriptor(CHAR(36))

    def process_bind_param(self, value, dialect):
        if value is None:
            return value
        if not isinstance(value, uuid.UUID):
            return str(uuid.UUID(value))
        return str(value)

    def process_result_value(self, value, dialect):
        if value is None:
            return value
        if not isinstance(value, uuid.UUID):
            value = uuid.UUID(value)
        return value

修改基表中的字段定义,替换为自定义的GUID类型:

class GUIDModel(SQLModel):
    """
    提供以GUID作为主键的表基类
    """

    guid: Optional[UUID] = Field(
        ...,
        primary_key=True,
        description=DescriptionConstants.GUID,
        sa_column=Column(
            "guid",
            GUID(),  # 使用自定义GUID类型
            nullable=False,
            primary_key=True,
            server_default=text("newsequentialid()"),
        ),
    )

方法2:利用SQLModel的Pydantic自动转换

如果模型正确继承了SQLModel(集成了Pydantic验证逻辑),可以在查询后通过模型的model_validate方法触发类型转换:

def test_get_project(self):
    with Session(__get_engine()) as session:
        project_data = Projects._get_project(session)
        project: Project = Project.model_validate(project_data)
        self.assertEqual(type(project.guid), uuid.UUID)

这种方法是在应用层做转换,不如自定义SQLAlchemy类型从数据库层面处理彻底。

验证

修改后重新运行测试,project.guid会自动转换为uuid.UUID类型,断言即可通过。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 01:33:35