Validation Errors¶
When request data fails Pydantic validation, django-pydantic-client returns a structured HTTP 422 Unprocessable Entity response automatically. This page explains the error format and how to customise it.
How it works¶
PydanticValidationMiddleware is auto-installed by django_pydantic's AppConfig.ready(). It hooks into Django's process_exception mechanism:
- Your view calls
MySchema(request). - Pydantic raises
ValidationErrorif data is invalid. - The middleware catches it before Django's 500 handler.
- A
JsonResponse({"detail": [...]}, status=422)is returned.
Your view function never executes past the schema line on invalid input.
Error response format¶
{
"detail": [
{
"type": "missing",
"loc": ["username"],
"msg": "Field required",
"input": {}
},
{
"type": "string_too_short",
"loc": ["password"],
"msg": "String should have at least 8 characters",
"input": "abc",
"ctx": {"min_length": 8}
}
]
}
| Key | Description |
|---|---|
type |
Pydantic error type identifier |
loc |
List of field names pointing to the failing field |
msg |
Human-readable error message |
input |
The value that was provided |
ctx |
Extra context (constraint values, etc.) |
Multiple errors at once¶
Pydantic validates all fields before raising — you get all errors in one response, not just the first:
{
"detail": [
{"type": "missing", "loc": ["username"], "msg": "Field required"},
{"type": "missing", "loc": ["email"], "msg": "Field required"},
{"type": "missing", "loc": ["age"], "msg": "Field required"}
]
}
This is ideal for front-ends: one round-trip reveals all broken fields.
Nested field locations¶
For nested models, loc is a path:
class AddressSchema(BaseModel):
city: str
zip_code: str = Field(pattern=r"^\d{5}$")
class OrderSchema(RequestModel):
address: AddressSchema
{
"detail": [
{
"type": "string_pattern_mismatch",
"loc": ["address", "zip_code"],
"msg": "String should match pattern '^\\d{5}$'"
}
]
}
Handling errors in the view (opt-in)¶
If you want to handle validation errors yourself instead of relying on the middleware, use a try/except:
from pydantic import ValidationError
from django.http import JsonResponse
from .schema import SignupSchema
def signup(request):
try:
data = SignupSchema(request)
except ValidationError as exc:
# Custom error handling
errors = {e["loc"][0]: e["msg"] for e in exc.errors(include_url=False)}
return JsonResponse({"errors": errors}, status=400)
# proceed...
return JsonResponse({"ok": True})
Tip
When you catch ValidationError yourself, the middleware is bypassed for that view.
Custom error messages with Field¶
from pydantic import Field
from django_pydantic import RequestModel
class SignupSchema(RequestModel):
username: str = Field(
min_length=3,
max_length=32,
description="3–32 characters, letters and numbers only",
)
age: int = Field(ge=18, description="Must be 18 or older")
Pydantic generates human-readable messages for all built-in constraints automatically.
Custom validators¶
from pydantic import field_validator
from django_pydantic import RequestModel
class SignupSchema(RequestModel):
username: str
password: str
confirm_password: str
@field_validator("username")
@classmethod
def username_alphanumeric(cls, v: str) -> str:
if not v.isalnum():
raise ValueError("Username must contain only letters and numbers")
return v.lower()
Next: URL Parameters →