使用SQLAlchemy MappedAsDataclass作为模型时init=False字段失效问题
解决SQLAlchemy Identity字段与FastAPI请求验证冲突问题
问题背景
使用SQLAlchemy的MappedAsDataclass定义数据库模型,同时将该模型作为FastAPI的请求体做参数验证时,遇到Identity自增字段id的必填矛盾:
- 给
id设置init=False,FastAPI仍提示id字段必填 - 移除
init=False,则提示__init__()缺少id参数
解决方案
无需重复定义Pydantic模型,通过typing.Annotated结合Pydantic的Field,同时为SQLAlchemy和Pydantic指定字段规则即可解决:
修改后的完整代码
from sqlalchemy import Identity from sqlalchemy.orm import DeclarativeBase from sqlalchemy.orm import Mapped from sqlalchemy.orm import mapped_column from sqlalchemy.orm import MappedAsDataclass from fastapi import FastAPI from fastapi.testclient import TestClient from pydantic import Field from typing import Annotated class Base(MappedAsDataclass, DeclarativeBase): pass class User(Base): __tablename__ = "user_account" # 使用Annotated整合SQLAlchemy映射规则与Pydantic验证规则 id: Mapped[Annotated[int, Field(default=None, exclude=True)]] = mapped_column( init=False, primary_key=True, server_default=Identity(always=True, start=1, increment=1, minvalue=1, cycle=False) ) name: Mapped[str] app = FastAPI() @app.post("/user") def create_user(user: User): return True client = TestClient(app) def test_add_user(): response = client.post("/user", json={"name": "john"}) assert response.status_code == 200
关键规则说明
Annotated[int, Field(default=None, exclude=True)]:- 告诉Pydantic该字段默认值为
None,且解析请求体时直接排除该字段,不需要前端传入
- 告诉Pydantic该字段默认值为
init=False:- 告诉SQLAlchemy创建
User实例时不需要传入id参数
- 告诉SQLAlchemy创建
server_default=Identity(...):- 配置数据库自动生成自增的
id值
- 配置数据库自动生成自增的
这套配置既保证了数据库模型的正确性,又让FastAPI的请求验证自动忽略id字段,完全不需要重复定义单独的Pydantic请求模型。
内容的提问来源于stack exchange,提问作者Roberto Falk
相关产品推荐
相关产品推荐

