import ali

This commit is contained in:
2026-02-01 01:45:51 +01:00
parent bdbfa4e25a
commit 46d6d88ce5
48 changed files with 6714 additions and 185 deletions
+61 -1
View File
@@ -5,8 +5,9 @@ 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 typing import Any
from pydantic import BaseModel, ConfigDict, Field
from pydantic import BaseModel, ConfigDict, Field, model_validator
from app.models.item import ItemStatus
from app.schemas.category import CategoryResponse
@@ -26,6 +27,7 @@ class ItemBase(BaseModel):
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")
characteristics: dict[str, str] | None = Field(None, description="Caractéristiques techniques (clé-valeur)")
notes: str | None = Field(None, description="Notes libres")
@@ -34,6 +36,8 @@ class ItemCreate(ItemBase):
category_id: int = Field(..., description="ID de la catégorie")
location_id: int = Field(..., description="ID de l'emplacement")
parent_item_id: int | None = Field(None, description="ID de l'objet parent (si intégré)")
shop_id: int | None = Field(None, description="ID de la boutique d'achat")
class ItemUpdate(BaseModel):
@@ -49,9 +53,12 @@ class ItemUpdate(BaseModel):
url: str | None = Field(None, max_length=500)
price: Decimal | None = Field(None, ge=0)
purchase_date: date | None = None
characteristics: dict[str, str] | None = None
notes: str | None = None
category_id: int | None = None
location_id: int | None = None
parent_item_id: int | None = None
shop_id: int | None = None
class ItemResponse(ItemBase):
@@ -62,6 +69,8 @@ class ItemResponse(ItemBase):
id: int
category_id: int
location_id: int
parent_item_id: int | None = None
shop_id: int | None = None
created_at: datetime
updated_at: datetime
@@ -71,6 +80,57 @@ class ItemWithRelations(ItemResponse):
category: CategoryResponse
location: LocationResponse
thumbnail_id: int | None = None
parent_item_name: str | None = None
@model_validator(mode="before")
@classmethod
def extract_computed_fields(cls, data: Any) -> Any:
"""Extrait les champs calculés : thumbnail et nom du parent."""
from sqlalchemy.orm import InstanceState
thumbnail_id = None
parent_item_name = None
# Vérifier que les relations sont chargées (éviter lazy load en async)
loaded_relations: set[str] = set()
if hasattr(data, "_sa_instance_state"):
state: InstanceState = data._sa_instance_state
loaded_relations = set(state.dict.keys())
if "documents" in loaded_relations:
for doc in data.documents:
if doc.type.value == "photo":
thumbnail_id = doc.id
break
if "parent_item" in loaded_relations and data.parent_item is not None:
parent_item_name = data.parent_item.name
if isinstance(data, dict):
if thumbnail_id:
data["thumbnail_id"] = thumbnail_id
if parent_item_name:
data["parent_item_name"] = parent_item_name
elif thumbnail_id or parent_item_name:
result = {}
for k in dir(data):
if k.startswith("_"):
continue
# Ne pas accéder aux relations non chargées
if k in ("documents", "parent_item", "children", "shop"):
continue
try:
result[k] = getattr(data, k)
except Exception:
pass
if thumbnail_id:
result["thumbnail_id"] = thumbnail_id
if parent_item_name:
result["parent_item_name"] = parent_item_name
return result
return data
class ItemSummary(BaseModel):
+45
View File
@@ -0,0 +1,45 @@
"""Schémas Pydantic pour les boutiques."""
from datetime import datetime
from pydantic import BaseModel, ConfigDict, Field
class ShopBase(BaseModel):
"""Schéma de base pour les boutiques."""
name: str = Field(..., min_length=1, max_length=200, description="Nom de la boutique")
description: str | None = Field(None, max_length=1000, description="Description optionnelle")
url: str | None = Field(None, max_length=500, description="URL du site web")
address: str | None = Field(None, description="Adresse physique")
class ShopCreate(ShopBase):
"""Schéma pour la création d'une boutique."""
pass
class ShopUpdate(BaseModel):
"""Schéma pour la mise à jour d'une boutique (tous les champs optionnels)."""
name: str | None = Field(None, min_length=1, max_length=200)
description: str | None = Field(None, max_length=1000)
url: str | None = Field(None, max_length=500)
address: str | None = None
class ShopResponse(ShopBase):
"""Schéma de réponse pour une boutique."""
model_config = ConfigDict(from_attributes=True)
id: int
created_at: datetime
updated_at: datetime
class ShopWithItemCount(ShopResponse):
"""Schéma de réponse avec le nombre d'objets."""
item_count: int = Field(default=0, description="Nombre d'objets achetés dans cette boutique")