FastAPI通过SQLModel向PostgreSQL存储List/Set类型数据报错如何解决
解决方案
核心思路是利用SQLModel兼容SQLAlchemy原生字段定义的特性,显式指定PostgreSQL数组类型,同时保留上层Pydantic的类型校验能力。
步骤1:导入依赖
from sqlalchemy.dialects.postgresql import ARRAY from sqlmodel import SQLModel, Field, Column
步骤2:修改模型字段定义
需要注意两个点:
- 用
sa_column参数显式指定数据库层使用PostgreSQL的ARRAY类型 - 可变类型的默认值必须用
default_factory声明,避免多实例共享默认对象的bug
修改后的完整模型如下:
from datetime import datetime from typing import List, Optional, Set from sqlalchemy.dialects.postgresql import ARRAY from sqlmodel import SQLModel, Field, Column class ProjectBase(SQLModel): name: Optional[str] summary: Optional[str] description: Optional[str] # 声明category字段数据库层为text数组,Pydantic层仍保留Set[str]校验 category: Set[str] = Field( default_factory=set, sa_column=Column(ARRAY(str), nullable=False, server_default="{}") ) award: Optional[str] = None url: Optional[str] = None published: datetime = Field(default_factory=datetime.utcnow) image: str = "placeholderMainImage" # 声明images字段数据库层为text数组,Pydantic层仍保留List[str]校验 images: List[str] = Field( default_factory=list, sa_column=Column(ARRAY(str), nullable=False, server_default="{}") ) learning: Optional[str] tech: Optional[str] tools: Optional[str] class Project(ProjectBase, table=True): id: int = Field(default=None, primary_key=True) created_at: datetime = Field(default_factory=datetime.utcnow) updated_at: datetime = Field(default_factory=datetime.utcnow)
适配说明
- 类型校验保留:上层FastAPI接口仍会按照
Set[str]和List[str]的规则校验入参,不会影响原有接口的校验逻辑 - 数据库存储适配:asyncpg原生支持PostgreSQL数组类型,不需要额外配置,数据会直接以数组格式存入数据库,不会再触发
expected str, got set的报错 - Set类型兼容:PostgreSQL没有原生的Set类型,会以数组格式存储,Pydantic层会自动将从数据库读取的数组转换为Set类型,自动完成去重逻辑,上层使用不受影响
后续操作
修改模型后重新生成表结构,或者手动将原有表的category、images字段从VARCHAR改为TEXT[]即可正常使用。
内容的提问来源于stack exchange,提问作者Juan David Garavito Espejo
相关产品推荐
相关产品推荐

