L’interface est localisée ; les présentations et documents des Skills sont traduits automatiquement par IA dans votre langue. Le code, les commandes, chemins et termes techniques restent en version originale, et le document de l’auteur fait foi.
Explorer les Skills
Données et recherche · SKILL.md

pydantic-models-py

pydantic-models-py

Pydantic v2 multi-model pattern for clean API contracts — Base, Create, Update, Response variants with camelCase aliases and PATCH support. Use when defining FastAPI request/response schemas or data validation models.

Independent GitHub snapshot · Automated classification · Original documentation governs

Documentation de la Skill

GitHub

name: pydantic-models-py description: Pydantic v2 multi-model pattern for clean API contracts — Base, Create, Update, Response variants with camelCase aliases and PATCH support. Use when defining FastAPI request/response schemas or data validation models. allowed-tools: Read, Write, Edit metadata: triggers: Pydantic models, API schema, request body, response model, Pydantic BaseModel, data validation, PATCH endpoint, camelCase alias, API contract, request/response schema related-skills: python-dev, openapi-spec-generation, api-design-principles domain: backend role: specialist scope: implementation output-format: code last-reviewed: "2026-03-14"

Iron Law

NEVER USE A SINGLE PYDANTIC MODEL FOR BOTH CREATE AND RESPONSE — separate contracts prevent leaking internal fields, block over-posting, and make API evolution safe

Pydantic v2 Models

Create Pydantic models following the multi-model pattern for clean, safe API contracts.

Multi-Model Pattern

ModelPurposeRequired Fields
BaseCommon fields shared across all variantsShared, validated fields
CreatePOST request body — fields the client suppliesRequired fields only
UpdatePATCH request body — partial updateAll optional (use None default)
ResponseAPI response — all fields the client seesIncludes id, timestamps
InDBInternal/repository layer — DO NOT expose directlyDB-only fields (e.g., hashed passwords)

Full Pattern Example

from datetime import datetime
from uuid import UUID
from pydantic import BaseModel, Field, ConfigDict


# ── Base: shared validated fields ──────────────────────────────────────────
class UserBase(BaseModel):
    name: str = Field(..., min_length=1, max_length=100)
    email: str = Field(..., pattern=r"^[^@]+@[^@]+\.[^@]+quot;)


# ── Create: POST /users body ────────────────────────────────────────────────
class UserCreate(UserBase):
    """Request body for user creation. Never include id or timestamps."""
    password: str = Field(..., min_length=8)


# ── Update: PATCH /users/{id} body ─────────────────────────────────────────
class UserUpdate(BaseModel):
    """All fields optional — client sends only what changes."""
    name: str | None = Field(None, min_length=1, max_length=100)
    email: str | None = Field(None, pattern=r"^[^@]+@[^@]+\.[^@]+quot;)


# ── Response: GET /users or POST /users response ────────────────────────────
class UserResponse(UserBase):
    """What the API returns. Never include password or internal fields."""
    id: UUID
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)  # ORM compatibility


# ── InDB: internal repository layer only ────────────────────────────────────
class UserInDB(UserResponse):
    """Internal model for repository layer. NEVER return this from a route."""
    hashed_password: str

camelCase Aliases (for JS/TS clients)

from pydantic import BaseModel, Field, ConfigDict


class UserResponse(BaseModel):
    workspace_id: str = Field(..., alias="workspaceId")
    created_at: datetime = Field(..., alias="createdAt")
    updated_at: datetime = Field(..., alias="updatedAt")

    model_config = ConfigDict(
        populate_by_name=True,  # Accept both snake_case and camelCase on input
    )

Use model.model_dump(by_alias=True) when serializing for JSON responses to emit camelCase.


PATCH Endpoint — Partial Update Pattern

from pydantic import BaseModel, Field


class ProjectUpdate(BaseModel):
    """All fields optional for PATCH requests. None means 'do not change'."""
    name: str | None = Field(None, min_length=1)
    description: str | None = None
    is_active: bool | None = None

In the service layer, apply only the non-None fields:

async def update_project(id: UUID, payload: ProjectUpdate, session: AsyncSession) -> Project:
    project = await session.get(Project, id)
    update_data = payload.model_dump(exclude_unset=True)  # Only fields client sent
    for field, value in update_data.items():
        setattr(project, field, value)
    await session.commit()
    await session.refresh(project)
    return project

PostgreSQL / SQLAlchemy Integration

from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from sqlalchemy import String, DateTime, func
from uuid import UUID
import uuid


class Base(DeclarativeBase):
    pass


class UserORM(Base):
    __tablename__ = "users"

    id: Mapped[UUID] = mapped_column(primary_key=True, default=uuid.uuid4)
    name: Mapped[str] = mapped_column(String(100), nullable=False)
    email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False)
    hashed_password: Mapped[str] = mapped_column(String(255), nullable=False)
    created_at: Mapped[datetime] = mapped_column(server_default=func.now())
    updated_at: Mapped[datetime] = mapped_column(server_default=func.now(), onupdate=func.now())


# Convert ORM → Pydantic Response (model_config from_attributes=True required)
user_response = UserResponse.model_validate(user_orm_instance)

Integration Steps

  1. Create models in src/<service>/schemas/<resource>.py
  2. Export from src/<service>/schemas/__init__.py
  3. Import in routes: from src.<service>.schemas.<resource> import UserCreate, UserResponse
  4. Use Create as request body param, Response as return type annotation
  5. Use model_validate(orm_obj) in service layer to convert ORM → Response

Naming Conventions

ModelNaming PatternExample
Base{Resource}BaseUserBase
Create{Resource}CreateUserCreate
Update{Resource}UpdateUserUpdate
Response{Resource}ResponseUserResponse
InDB{Resource}InDBUserInDB

Hard Prohibitions

  • Never return InDB from a route handler — it contains internal fields (hashed passwords, etc.)
  • Never use Optional[str] — use str | None (Python 3.10+ union syntax)
  • Never use @validator — this is Pydantic v1 syntax; use @field_validator in v2
  • Never use orm_mode = True — use model_config = ConfigDict(from_attributes=True) in v2
  • Never skip exclude_unset=True in PATCH service logic — it distinguishes "not sent" from null
Skills enregistrées