generated from gilles/template-webapp
claude code
This commit is contained in:
68
backend/app/schemas/__init__.py
Normal file
68
backend/app/schemas/__init__.py
Normal file
@@ -0,0 +1,68 @@
|
||||
"""Package des schémas Pydantic."""
|
||||
|
||||
from app.schemas.category import (
|
||||
CategoryCreate,
|
||||
CategoryResponse,
|
||||
CategoryUpdate,
|
||||
CategoryWithItemCount,
|
||||
)
|
||||
from app.schemas.common import (
|
||||
ErrorResponse,
|
||||
PaginatedResponse,
|
||||
PaginationParams,
|
||||
SuccessResponse,
|
||||
)
|
||||
from app.schemas.document import (
|
||||
DocumentCreate,
|
||||
DocumentResponse,
|
||||
DocumentUpdate,
|
||||
DocumentUploadResponse,
|
||||
)
|
||||
from app.schemas.item import (
|
||||
ItemCreate,
|
||||
ItemFilter,
|
||||
ItemResponse,
|
||||
ItemSummary,
|
||||
ItemUpdate,
|
||||
ItemWithRelations,
|
||||
)
|
||||
from app.schemas.location import (
|
||||
LocationCreate,
|
||||
LocationResponse,
|
||||
LocationTree,
|
||||
LocationUpdate,
|
||||
LocationWithChildren,
|
||||
LocationWithItemCount,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
# Category
|
||||
"CategoryCreate",
|
||||
"CategoryUpdate",
|
||||
"CategoryResponse",
|
||||
"CategoryWithItemCount",
|
||||
# Location
|
||||
"LocationCreate",
|
||||
"LocationUpdate",
|
||||
"LocationResponse",
|
||||
"LocationWithChildren",
|
||||
"LocationWithItemCount",
|
||||
"LocationTree",
|
||||
# Item
|
||||
"ItemCreate",
|
||||
"ItemUpdate",
|
||||
"ItemResponse",
|
||||
"ItemWithRelations",
|
||||
"ItemSummary",
|
||||
"ItemFilter",
|
||||
# Document
|
||||
"DocumentCreate",
|
||||
"DocumentUpdate",
|
||||
"DocumentResponse",
|
||||
"DocumentUploadResponse",
|
||||
# Common
|
||||
"PaginationParams",
|
||||
"PaginatedResponse",
|
||||
"ErrorResponse",
|
||||
"SuccessResponse",
|
||||
]
|
||||
48
backend/app/schemas/category.py
Normal file
48
backend/app/schemas/category.py
Normal file
@@ -0,0 +1,48 @@
|
||||
"""Schémas Pydantic pour les catégories.
|
||||
|
||||
Définit les schémas de validation pour les requêtes et réponses API.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class CategoryBase(BaseModel):
|
||||
"""Schéma de base pour les catégories."""
|
||||
|
||||
name: str = Field(..., min_length=1, max_length=100, description="Nom de la catégorie")
|
||||
description: str | None = Field(None, max_length=1000, description="Description optionnelle")
|
||||
color: str | None = Field(None, pattern=r"^#[0-9A-Fa-f]{6}$", description="Couleur hex (#RRGGBB)")
|
||||
icon: str | None = Field(None, max_length=50, description="Nom de l'icône")
|
||||
|
||||
|
||||
class CategoryCreate(CategoryBase):
|
||||
"""Schéma pour la création d'une catégorie."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class CategoryUpdate(BaseModel):
|
||||
"""Schéma pour la mise à jour d'une catégorie (tous les champs optionnels)."""
|
||||
|
||||
name: str | None = Field(None, min_length=1, max_length=100)
|
||||
description: str | None = Field(None, max_length=1000)
|
||||
color: str | None = Field(None, pattern=r"^#[0-9A-Fa-f]{6}$")
|
||||
icon: str | None = Field(None, max_length=50)
|
||||
|
||||
|
||||
class CategoryResponse(CategoryBase):
|
||||
"""Schéma de réponse pour une catégorie."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
|
||||
class CategoryWithItemCount(CategoryResponse):
|
||||
"""Schéma de réponse avec le nombre d'objets."""
|
||||
|
||||
item_count: int = Field(default=0, description="Nombre d'objets dans cette catégorie")
|
||||
60
backend/app/schemas/common.py
Normal file
60
backend/app/schemas/common.py
Normal file
@@ -0,0 +1,60 @@
|
||||
"""Schémas Pydantic communs.
|
||||
|
||||
Définit les schémas réutilisables (pagination, erreurs, etc.).
|
||||
"""
|
||||
|
||||
from typing import Generic, TypeVar
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class PaginationParams(BaseModel):
|
||||
"""Paramètres de pagination."""
|
||||
|
||||
page: int = Field(default=1, ge=1, description="Numéro de page (commence à 1)")
|
||||
page_size: int = Field(default=20, ge=1, le=100, description="Nombre d'éléments par page")
|
||||
|
||||
@property
|
||||
def offset(self) -> int:
|
||||
"""Calcule l'offset pour la requête SQL."""
|
||||
return (self.page - 1) * self.page_size
|
||||
|
||||
|
||||
class PaginatedResponse(BaseModel, Generic[T]):
|
||||
"""Réponse paginée générique."""
|
||||
|
||||
items: list[T]
|
||||
total: int = Field(..., description="Nombre total d'éléments")
|
||||
page: int = Field(..., description="Page actuelle")
|
||||
page_size: int = Field(..., description="Taille de la page")
|
||||
pages: int = Field(..., description="Nombre total de pages")
|
||||
|
||||
@classmethod
|
||||
def create(
|
||||
cls, items: list[T], total: int, page: int, page_size: int
|
||||
) -> "PaginatedResponse[T]":
|
||||
"""Crée une réponse paginée."""
|
||||
pages = (total + page_size - 1) // page_size if page_size > 0 else 0
|
||||
return cls(
|
||||
items=items,
|
||||
total=total,
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
pages=pages,
|
||||
)
|
||||
|
||||
|
||||
class ErrorResponse(BaseModel):
|
||||
"""Schéma de réponse d'erreur."""
|
||||
|
||||
detail: str = Field(..., description="Message d'erreur")
|
||||
type: str = Field(..., description="Type d'erreur")
|
||||
|
||||
|
||||
class SuccessResponse(BaseModel):
|
||||
"""Schéma de réponse de succès."""
|
||||
|
||||
message: str = Field(..., description="Message de succès")
|
||||
id: int | None = Field(None, description="ID de l'élément concerné")
|
||||
63
backend/app/schemas/document.py
Normal file
63
backend/app/schemas/document.py
Normal file
@@ -0,0 +1,63 @@
|
||||
"""Schémas Pydantic pour les documents attachés.
|
||||
|
||||
Définit les schémas de validation pour les requêtes et réponses API.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from app.models.document import DocumentType
|
||||
|
||||
|
||||
class DocumentBase(BaseModel):
|
||||
"""Schéma de base pour les documents."""
|
||||
|
||||
type: DocumentType = Field(..., description="Type de document")
|
||||
description: str | None = Field(None, max_length=500, description="Description optionnelle")
|
||||
|
||||
|
||||
class DocumentCreate(DocumentBase):
|
||||
"""Schéma pour la création d'un document (métadonnées seulement).
|
||||
|
||||
Le fichier est uploadé séparément via multipart/form-data.
|
||||
"""
|
||||
|
||||
item_id: int = Field(..., description="ID de l'objet associé")
|
||||
|
||||
|
||||
class DocumentUpdate(BaseModel):
|
||||
"""Schéma pour la mise à jour d'un document."""
|
||||
|
||||
type: DocumentType | None = None
|
||||
description: str | None = Field(None, max_length=500)
|
||||
|
||||
|
||||
class DocumentResponse(BaseModel):
|
||||
"""Schéma de réponse pour un document."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
filename: str
|
||||
original_name: str
|
||||
type: DocumentType
|
||||
mime_type: str
|
||||
size_bytes: int
|
||||
file_path: str
|
||||
description: str | None
|
||||
item_id: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
|
||||
class DocumentUploadResponse(BaseModel):
|
||||
"""Schéma de réponse après upload d'un document."""
|
||||
|
||||
id: int
|
||||
filename: str
|
||||
original_name: str
|
||||
type: DocumentType
|
||||
mime_type: str
|
||||
size_bytes: int
|
||||
message: str = "Document uploadé avec succès"
|
||||
98
backend/app/schemas/item.py
Normal file
98
backend/app/schemas/item.py
Normal file
@@ -0,0 +1,98 @@
|
||||
"""Schémas Pydantic pour les objets d'inventaire.
|
||||
|
||||
Définit les schémas de validation pour les requêtes et réponses API.
|
||||
"""
|
||||
|
||||
from datetime import date, datetime
|
||||
from decimal import Decimal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from app.models.item import ItemStatus
|
||||
from app.schemas.category import CategoryResponse
|
||||
from app.schemas.location import LocationResponse
|
||||
|
||||
|
||||
class ItemBase(BaseModel):
|
||||
"""Schéma de base pour les objets."""
|
||||
|
||||
name: str = Field(..., min_length=1, max_length=200, description="Nom de l'objet")
|
||||
description: str | None = Field(None, description="Description détaillée")
|
||||
quantity: int = Field(default=1, ge=0, description="Quantité en stock")
|
||||
status: ItemStatus = Field(default=ItemStatus.IN_STOCK, description="Statut de l'objet")
|
||||
brand: str | None = Field(None, max_length=100, description="Marque")
|
||||
model: str | None = Field(None, max_length=100, description="Modèle")
|
||||
serial_number: str | None = Field(None, max_length=100, description="Numéro de série")
|
||||
url: str | None = Field(None, max_length=500, description="Lien vers page produit")
|
||||
price: Decimal | None = Field(None, ge=0, decimal_places=2, description="Prix d'achat")
|
||||
purchase_date: date | None = Field(None, description="Date d'achat")
|
||||
notes: str | None = Field(None, description="Notes libres")
|
||||
|
||||
|
||||
class ItemCreate(ItemBase):
|
||||
"""Schéma pour la création d'un objet."""
|
||||
|
||||
category_id: int = Field(..., description="ID de la catégorie")
|
||||
location_id: int = Field(..., description="ID de l'emplacement")
|
||||
|
||||
|
||||
class ItemUpdate(BaseModel):
|
||||
"""Schéma pour la mise à jour d'un objet (tous les champs optionnels)."""
|
||||
|
||||
name: str | None = Field(None, min_length=1, max_length=200)
|
||||
description: str | None = None
|
||||
quantity: int | None = Field(None, ge=0)
|
||||
status: ItemStatus | None = None
|
||||
brand: str | None = Field(None, max_length=100)
|
||||
model: str | None = Field(None, max_length=100)
|
||||
serial_number: str | None = Field(None, max_length=100)
|
||||
url: str | None = Field(None, max_length=500)
|
||||
price: Decimal | None = Field(None, ge=0)
|
||||
purchase_date: date | None = None
|
||||
notes: str | None = None
|
||||
category_id: int | None = None
|
||||
location_id: int | None = None
|
||||
|
||||
|
||||
class ItemResponse(ItemBase):
|
||||
"""Schéma de réponse pour un objet."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
category_id: int
|
||||
location_id: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
|
||||
class ItemWithRelations(ItemResponse):
|
||||
"""Schéma de réponse avec les relations (catégorie et emplacement)."""
|
||||
|
||||
category: CategoryResponse
|
||||
location: LocationResponse
|
||||
|
||||
|
||||
class ItemSummary(BaseModel):
|
||||
"""Schéma résumé pour les listes d'objets."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
name: str
|
||||
quantity: int
|
||||
status: ItemStatus
|
||||
brand: str | None
|
||||
category_id: int
|
||||
location_id: int
|
||||
|
||||
|
||||
class ItemFilter(BaseModel):
|
||||
"""Schéma pour filtrer les objets."""
|
||||
|
||||
category_id: int | None = None
|
||||
location_id: int | None = None
|
||||
status: ItemStatus | None = None
|
||||
search: str | None = Field(None, min_length=2, description="Recherche textuelle")
|
||||
min_price: Decimal | None = None
|
||||
max_price: Decimal | None = None
|
||||
70
backend/app/schemas/location.py
Normal file
70
backend/app/schemas/location.py
Normal file
@@ -0,0 +1,70 @@
|
||||
"""Schémas Pydantic pour les emplacements.
|
||||
|
||||
Définit les schémas de validation pour les requêtes et réponses API.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from app.models.location import LocationType
|
||||
|
||||
|
||||
class LocationBase(BaseModel):
|
||||
"""Schéma de base pour les emplacements."""
|
||||
|
||||
name: str = Field(..., min_length=1, max_length=100, description="Nom de l'emplacement")
|
||||
type: LocationType = Field(..., description="Type d'emplacement")
|
||||
description: str | None = Field(None, max_length=500, description="Description optionnelle")
|
||||
|
||||
|
||||
class LocationCreate(LocationBase):
|
||||
"""Schéma pour la création d'un emplacement."""
|
||||
|
||||
parent_id: int | None = Field(None, description="ID du parent (None si racine)")
|
||||
|
||||
|
||||
class LocationUpdate(BaseModel):
|
||||
"""Schéma pour la mise à jour d'un emplacement (tous les champs optionnels)."""
|
||||
|
||||
name: str | None = Field(None, min_length=1, max_length=100)
|
||||
type: LocationType | None = None
|
||||
description: str | None = Field(None, max_length=500)
|
||||
parent_id: int | None = None
|
||||
|
||||
|
||||
class LocationResponse(LocationBase):
|
||||
"""Schéma de réponse pour un emplacement."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
parent_id: int | None
|
||||
path: str
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
|
||||
class LocationWithChildren(LocationResponse):
|
||||
"""Schéma de réponse avec les enfants."""
|
||||
|
||||
children: list["LocationWithChildren"] = Field(default_factory=list)
|
||||
|
||||
|
||||
class LocationWithItemCount(LocationResponse):
|
||||
"""Schéma de réponse avec le nombre d'objets."""
|
||||
|
||||
item_count: int = Field(default=0, description="Nombre d'objets à cet emplacement")
|
||||
|
||||
|
||||
class LocationTree(BaseModel):
|
||||
"""Schéma pour l'arborescence complète des emplacements."""
|
||||
|
||||
id: int
|
||||
name: str
|
||||
type: LocationType
|
||||
path: str
|
||||
children: list["LocationTree"] = Field(default_factory=list)
|
||||
item_count: int = 0
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
Reference in New Issue
Block a user