add pydantic on backend

This commit is contained in:
david 2026-02-24 17:17:36 +03:00
parent abce2bb6ef
commit 580d04d1e7
4 changed files with 94 additions and 15 deletions

View file

@ -1,10 +1,13 @@
import time
from decimal import Decimal
from pydantic import ValidationError
from flask import Blueprint, request, jsonify
from flask_jwt_extended import jwt_required, get_jwt_identity, create_access_token, create_refresh_token
from app import db
from app.models import User, Product, OrderItem, Order
from app.celery import celery
from app.schemas import ProductCreateRequest, ProductResponse
api_bp = Blueprint("api", __name__)
@ -104,21 +107,28 @@ def create_product():
if not user or not user.is_admin:
return jsonify({"error": "Admin access required"}), 403
data = request.get_json()
try:
# Validate request data using Pydantic schema
product_data = ProductCreateRequest(**request.get_json())
product = Product(
name=data["name"],
description=data.get("description"),
price=data["price"],
stock=data.get("stock", 0),
image_url=data.get("image_url"),
category=data.get("category")
name=product_data.name,
description=product_data.description,
price=product_data.price,
stock=product_data.stock,
image_url=product_data.image_url,
category=product_data.category
)
db.session.add(product)
db.session.commit()
return jsonify(product.to_dict()), 201
# Use Pydantic schema for response
response = ProductResponse.model_validate(product)
return jsonify(response.model_dump()), 201
except ValidationError as e:
return jsonify({"error": "Validation error", "details": e.errors()}), 400
@api_bp.route("/products/<int:product_id>", methods=["PUT"])

View file

@ -0,0 +1,4 @@
"""Pydantic schemas for request/response validation"""
from app.schemas.product import ProductCreateRequest, ProductResponse
__all__ = ["ProductCreateRequest", "ProductResponse"]

View file

@ -0,0 +1,64 @@
"""Pydantic schemas for Product model"""
from pydantic import BaseModel, Field, field_validator
from decimal import Decimal
from datetime import datetime
from typing import Optional
class ProductCreateRequest(BaseModel):
"""Schema for creating a new product"""
name: str = Field(..., min_length=1, max_length=200, description="Product name")
description: Optional[str] = Field(None, description="Product description")
price: Decimal = Field(..., gt=0, description="Product price (must be greater than 0)")
stock: int = Field(default=0, ge=0, description="Product stock quantity")
image_url: Optional[str] = Field(None, max_length=500, description="Product image URL")
category: Optional[str] = Field(None, description="Product category")
class Config:
json_schema_extra = {
"example": {
"name": "Handcrafted Wooden Bowl",
"description": "A beautiful handcrafted bowl made from oak",
"price": 45.99,
"stock": 10,
"image_url": "https://example.com/bowl.jpg",
"category": "Woodwork"
}
}
@field_validator("price")
@classmethod
def validate_price(cls, v: Decimal) -> Decimal:
"""Validate that price has at most 2 decimal places"""
if v.as_tuple().exponent < -2:
raise ValueError("Price must have at most 2 decimal places")
return v
class ProductResponse(BaseModel):
"""Schema for product response"""
id: int
name: str
description: Optional[str] = None
price: float
stock: int
image_url: Optional[str] = None
is_active: bool
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
class Config:
from_attributes = True
json_schema_extra = {
"example": {
"id": 1,
"name": "Handcrafted Wooden Bowl",
"description": "A beautiful handcrafted bowl made from oak",
"price": 45.99,
"stock": 10,
"image_url": "https://example.com/bowl.jpg",
"is_active": True,
"created_at": "2024-01-15T10:30:00",
"updated_at": "2024-01-15T10:30:00"
}
}

View file

@ -8,3 +8,4 @@ python-dotenv==1.0.0
Werkzeug==3.0.1
SQLAlchemy==2.0.23
celery[redis]==5.3.6
pydantic==2.5.3