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
相关产品推荐
相关产品推荐

