Validation API
The Validation API provides base classes and utilities for request/response validation using Pydantic.
Base Classes
BaseDTO
BaseDTO
Bases: BaseModel
BaseResponse
BaseResponse
Bases: BaseModel
Example Usage
Basic DTO
from ascender.common import BaseDTO
from pydantic import Field, EmailStr
class CreateUserDTO(BaseDTO):
"""Data transfer object for creating a user."""
name: str = Field(..., min_length=2, max_length=100)
email: EmailStr = Field(...)
age: int = Field(..., ge=18, le=120)
bio: str | None = Field(None, max_length=500)
Response Models
from ascender.common import BaseResponse
from datetime import datetime
class UserResponse(BaseResponse):
"""User response model."""
id: str
name: str
email: str
age: int
created_at: datetime
updated_at: datetime
class UserListResponse(BaseResponse):
"""Paginated user list response."""
users: list[UserResponse]
total: int
page: int
page_size: int
Using in Controllers
from ascender.common import Controller, GET, POST, Body
from ascender.common import inject
@Controller("/users")
class UserController:
"""User management endpoints."""
def __init__(self, user_service=inject()):
self.user_service = user_service
@GET("/:id", response_model=UserResponse)
async def get_user(self, id: str):
"""Get user by ID with validated response."""
user = await self.user_service.find_by_id(id)
return UserResponse(**user)
@POST("/", response_model=UserResponse, status_code=201)
async def create_user(self, data: CreateUserDTO = Body(...)):
"""Create user with validated input."""
user = await self.user_service.create(data.model_dump())
return UserResponse(**user)
@GET("/", response_model=UserListResponse)
async def list_users(self, page: int = 1, limit: int = 10):
"""List users with pagination."""
users, total = await self.user_service.list(page, limit)
return UserListResponse(
users=[UserResponse(**u) for u in users],
total=total,
page=page,
page_size=limit
)
Custom Validators
from ascender.common import BaseDTO
from pydantic import Field, field_validator, model_validator
class CreateProductDTO(BaseDTO):
"""Product creation with custom validation."""
name: str = Field(..., min_length=3)
price: float = Field(..., gt=0)
discount_price: float | None = Field(None, gt=0)
category: str = Field(...)
@field_validator('category')
@classmethod
def validate_category(cls, v: str) -> str:
"""Validate category is in allowed list."""
allowed = ['electronics', 'clothing', 'food', 'books']
if v.lower() not in allowed:
raise ValueError(f'Category must be one of: {", ".join(allowed)}')
return v.lower()
@model_validator(mode='after')
def validate_discount_price(self):
"""Validate discount price is less than regular price."""
if self.discount_price and self.discount_price >= self.price:
raise ValueError('Discount price must be less than regular price')
return self
Nested Models
from ascender.common import BaseDTO, BaseResponse
class AddressDTO(BaseDTO):
"""Address information."""
street: str
city: str
state: str
zip_code: str
country: str = "USA"
class CreateUserDTO(BaseDTO):
"""User with nested address."""
name: str
email: str
address: AddressDTO
class AddressResponse(BaseResponse):
"""Address response."""
street: str
city: str
state: str
zip_code: str
country: str
class UserResponse(BaseResponse):
"""User response with nested address."""
id: str
name: str
email: str
address: AddressResponse
Optional and Default Values
from ascender.common import BaseDTO
from datetime import datetime
class UpdateUserDTO(BaseDTO):
"""Partial update with optional fields."""
name: str | None = None
email: str | None = None
age: int | None = None
bio: str | None = None
updated_at: datetime = Field(default_factory=datetime.now)
Configuration Options
from ascender.common import BaseDTO
from pydantic import ConfigDict
class StrictUserDTO(BaseDTO):
"""DTO with custom configuration."""
model_config = ConfigDict(
str_strip_whitespace=True, # Strip whitespace from strings
str_min_length=1, # Minimum string length
validate_assignment=True, # Validate on attribute assignment
extra='forbid', # Forbid extra fields
frozen=False, # Allow mutation
)
name: str
email: str
See Also
- Data Validation Guide - Comprehensive validation guide
- Pydantic Documentation - Official Pydantic docs
- Controllers - Using validation in controllers