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

PostgresDsn.build从Pydantic v1迁移至v2的问题排查与优化

Pydantic v2迁移:解决PostgresDsn返回MultiHostUrl致SQLAlchemy错误

问题概述

从FastAPI旧教程迁移配置类到Pydantic v2时,出现SQLAlchemy参数错误。核心代码及报错信息如下:

原代码片段

from pydantic import AnyHttpUrl, HttpUrl, PostgresDsn, field_validator
from pydantic_settings import BaseSettings
from pydantic_core.core_schema import FieldValidationInfo
from typing import Optional, Any
import os
from dotenv import load_dotenv

load_dotenv()


class Settings(BaseSettings):
    # 省略其他配置项
    POSTGRES_SERVER: str = 'localhost:5432'
    POSTGRES_USER: str = os.getenv('POSTGRES_USER')
    POSTGRES_PASSWORD: str = os.getenv('POSTGRES_PASSWORD')
    POSTGRES_DB: str = os.getenv('POSTGRES_DB')
    SQLALCHEMY_DATABASE_URI: Optional[PostgresDsn] = None

    @field_validator("SQLALCHEMY_DATABASE_URI", mode='before')
    @classmethod
    def assemble_db_connection(cls, v: Optional[str], info: FieldValidationInfo) -> Any:
        if isinstance(v, str):
            return v
        postgres_dsn = PostgresDsn.build(
            scheme="postgresql",
            username=info.data.get("POSTGRES_USER"),
            password=info.data.get("POSTGRES_PASSWORD"),
            host=info.data.get("POSTGRES_SERVER"),
            path=f"{info.data.get('POSTGRES_DB') or ''}",
        )
        return str(postgres_dsn)

报错信息

sqlalchemy.exc.ArgumentError: Expected string or URL object, got MultiHostUrl('postgresql://user:password@localhost:5432/database')

临时解决方法

将SQLALCHEMY_DATABASE_URI的类型从Optional[PostgresDsn]改为Optional[str],依赖Pydantic自动转换解决问题,但不确定是否为最优方案。

更优处理方案

方案1:保留PostgresDsn校验,确保输出字符串

Pydantic v2中,PostgresDsn.build()返回MultiHostUrl对象,即使验证器返回str(postgres_dsn),字段类型为PostgresDsn时,Pydantic会再次将字符串解析为MultiHostUrl实例,导致传给SQLAlchemy的不是字符串。

修改验证器的mode为after,在字段解析完成后转为字符串:

@field_validator("SQLALCHEMY_DATABASE_URI", mode='after')
@classmethod
def ensure_string_uri(cls, v: Optional[PostgresDsn]) -> Optional[str]:
    return str(v) if v else None

同时保留字段类型Optional[PostgresDsn] = None,这样既利用PostgresDsn的格式校验能力,又确保最终传递给SQLAlchemy的是字符串。

方案2:直接使用字符串类型(简洁高效)

如果不需要Pydantic对URI做格式校验,直接将字段类型设为Optional[str]是完全合理的方案。验证器已负责拼接正确的URI格式,此时字符串类型注释符合业务逻辑,也是官方示例中的常见写法。

方案3:使用通用Url类型并指定scheme

替换PostgresDsn为Url类型,指定scheme为postgresql,同时调用unicode_string()返回字符串:

from pydantic import Url

class Settings(BaseSettings):
    # ...其他配置项
    SQLALCHEMY_DATABASE_URI: Optional[Url] = None

    @field_validator("SQLALCHEMY_DATABASE_URI", mode='before')
    @classmethod
    def assemble_db_connection(cls, v: Optional[str], info: FieldValidationInfo) -> Any:
        if isinstance(v, str):
            return v
        return Url.build(
            scheme="postgresql",
            username=info.data.get("POSTGRES_USER"),
            password=info.data.get("POSTGRES_PASSWORD"),
            host=info.data.get("POSTGRES_SERVER"),
            path=f"{info.data.get('POSTGRES_DB') or ''}",
        ).unicode_string()

总结

  • 需保留URI格式校验:选方案1,兼顾校验与字符串输出;
  • 无需额外校验:选方案2,简洁高效;
  • 需灵活URL处理:选方案3。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 16:57:54