Keep Django serializer validation errors useful to a form

An API can return several useful validation errors without exposing a stack trace. Keep field-specific failures attached to their field names, and reserve a separate location for rules involving more than one field.
Field and cross-field validation have separate locations in the API error contract.
Validate the submitted shape
This Django REST Framework serializer uses a synthetic booking request. It validates contact details and a cross-field time rule.
from rest_framework import serializers
class BookingInput(serializers.Serializer):
name = serializers.CharField(max_length=100)
email = serializers.EmailField()
starts_at = serializers.DateTimeField()
ends_at = serializers.DateTimeField()
def validate(self, attrs):
if attrs["ends_at"] <= attrs["starts_at"]:
raise serializers.ValidationError(
"End time must be after start time"
)
return attrs
serializer = BookingInput(data={
"name": "Fixture person",
"email": "invalid",
"starts_at": "2025-01-02T10:00:00Z",
"ends_at": "2025-01-02T09:00:00Z",
})
serializer.is_valid()
print(serializer.errors)
Associate each displayed error with its input through accessible markup, and keep the user's entered values available for correction. The invalid email is a field error. Once fields are valid, the object-level time rule can produce a non-field error. Do not assume every possible validation failure appears at once: cross-field validation may depend on field parsing succeeding first.
A frontend should map recognized field keys to their inputs and show non-field errors near the form as a whole. The exact non-field key is configurable, so keep the API contract explicit.
Preserve a useful error boundary
Return validation errors with a suitable client-error response. Keep unexpected exceptions in internal diagnostics with a correlation ID. A database traceback, provider credential or raw request payload should not become a user-facing validation message.
Set stable error codes when clients need programmatic behavior, instead of requiring them to compare English text. The displayed sentence can then change without silently breaking the form's logic.
Test an invalid email, a missing required field and a valid pair of timestamps in the wrong order. For each, assert where the error appears. Also test a malformed JSON body separately, since parsing fails before serializer validation.
On partial updates, cross-field validation needs existing instance values for omitted fields. The simple create serializer above intentionally avoids that extra behavior; copying it unchanged into PATCH handling would leave a gap.
The forms and API workflows in Nirogidhara provide related context. DRF's serializer guide explains field and object validation.