PEP 484 type stubs for Django REST Framework enabling static type checking with comprehensive type definitions for all major DRF components
—
Django REST Framework provides a comprehensive field system for data validation, serialization, and deserialization. The type stubs enable full type safety for field definitions, relational fields, and custom field implementations with precise generic type support.
class Field(Generic[_VT, _DT, _RP, _IN]):
"""
Base class for all serializer fields with generic type support.
Type Parameters:
_VT: Value Type (internal Python type)
_DT: Data Type (input data type)
_RP: Representation Type (output serialization type)
_IN: Instance Type (model instance type)
"""
# Core configuration
read_only: bool
write_only: bool
required: bool
default: Any
initial: Any
allow_null: bool
# Validation configuration
validators: list[Callable[[Any], Any]]
error_messages: dict[str, str]
# Display configuration
label: str | None
help_text: str | None
style: dict[str, Any]
def __init__(
self,
read_only: bool = False,
write_only: bool = False,
required: bool | None = None,
default: Any = empty,
initial: Any = empty,
source: str | None = None,
label: str | None = None,
help_text: str | None = None,
style: dict[str, Any] | None = None,
error_messages: dict[str, str] | None = None,
validators: list[Callable[[Any], Any]] | None = None,
allow_null: bool = False
) -> None: ...
def bind(self, field_name: str, parent: Any) -> None:
"""Bind field to parent serializer."""
...
def get_value(self, dictionary: dict[str, Any]) -> Any:
"""Extract value from input data."""
...
def get_attribute(self, instance: _IN) -> Any:
"""Get attribute value from object instance."""
...
def get_default(self) -> Any:
"""Get default value for field."""
...
def validate_empty_values(self, data: _DT) -> tuple[bool, Any]:
"""Validate empty/null values."""
...
def run_validation(self, data: _DT) -> _VT:
"""Run full validation pipeline."""
...
def run_validators(self, value: _VT) -> None:
"""Run field validators."""
...
def to_internal_value(self, data: _DT) -> _VT:
"""Convert input data to internal value."""
...
def to_representation(self, value: _VT) -> _RP:
"""Convert internal value to serialized representation."""
...
def fail(self, key: str, **kwargs: Any) -> NoReturn:
"""Raise validation error with message key."""
...
@property
def validators(self) -> list[Validator[_VT]]: ...
@validators.setter
def validators(self, validators: list[Validator[_VT]]) -> None: ...
def get_validators(self) -> list[Validator[_VT]]: ...
def get_initial(self) -> _VT | None: ...
@property
def root(self) -> BaseSerializer: ...
@property
def context(self) -> dict[str, Any]: ...Parameters:
read_only: bool - Field is excluded from input validationwrite_only: bool - Field is excluded from serialized outputrequired: bool | None - Field is required in input (default: True unless read_only or has default)default: Any - Default value when not providedsource: str | None - Source attribute path (defaults to field name)allow_null: bool - Allow None valuesclass BooleanField(Field[bool, str | bool | int, bool, Any]):
"""Boolean field that accepts various truthy/falsy inputs."""
TRUE_VALUES: set[str | int | bool]
FALSE_VALUES: set[str | int | bool]
def __init__(self, **kwargs: Any) -> None: ...
class NullBooleanField(Field[bool | None, str | bool | int | None, bool, Any]):
"""Boolean field that also accepts None."""
passclass CharField(Field[str, str, str, Any]):
"""Character field with length and whitespace validation."""
def __init__(
self,
*,
max_length: int | None = None,
min_length: int | None = None,
allow_blank: bool = False,
trim_whitespace: bool = True,
**kwargs: Any
) -> None: ...
class EmailField(CharField):
"""Email field with email validation."""
pass
class RegexField(CharField):
"""Field that validates against a regular expression."""
def __init__(
self,
regex: str | Pattern[str],
max_length: int | None = None,
min_length: int | None = None,
**kwargs: Any
) -> None: ...
class SlugField(CharField):
"""Field for URL slugs (alphanumeric + hyphens/underscores)."""
pass
class URLField(CharField):
"""URL field with URL validation."""
pass
class UUIDField(Field[uuid.UUID, uuid.UUID | str | int, str, Any]):
"""UUID field that accepts UUID objects or strings."""
def __init__(self, *, format: str = 'hex_verbose', **kwargs: Any) -> None: ...
class IPAddressField(CharField):
"""IP address field supporting IPv4 and IPv6."""
def __init__(self, *, protocol: str = 'both', **kwargs: Any) -> None: ...String Field Parameters:
max_length: int | None - Maximum string lengthmin_length: int | None - Minimum string lengthallow_blank: bool - Allow empty strings (default: False)trim_whitespace: bool - Trim leading/trailing whitespace (default: True)class IntegerField(Field[int, float | int | str, int, Any]):
"""Integer field with range validation."""
def __init__(
self,
*,
max_value: int | None = None,
min_value: int | None = None,
**kwargs: Any
) -> None: ...
class FloatField(Field[float, float | int | str, str, Any]):
"""Float field with range validation."""
def __init__(
self,
*,
max_value: float | None = None,
min_value: float | None = None,
**kwargs: Any
) -> None: ...
class DecimalField(Field[Decimal, int | float | str | Decimal, str, Any]):
"""Decimal field with precision control."""
def __init__(
self,
max_digits: int | None = None,
decimal_places: int | None = None,
coerce_to_string: bool | None = None,
max_value: int | float | Decimal | None = None,
min_value: int | float | Decimal | None = None,
localize: bool = False,
rounding: str | None = None,
**kwargs: Any
) -> None: ...Numeric Field Parameters:
max_value: int | float | None - Maximum allowed valuemin_value: int | float | None - Minimum allowed valuemax_digits: int | None - Maximum total digits (DecimalField)decimal_places: int | None - Maximum decimal places (DecimalField)class DateTimeField(Field[datetime.datetime, datetime.datetime | str, str, Any]):
"""DateTime field with format support."""
def __init__(
self,
*,
format: str | None = None,
input_formats: list[str] | None = None,
default_timezone: timezone | None = None,
**kwargs: Any
) -> None: ...
class DateField(Field[datetime.date, datetime.date | str, str, Any]):
"""Date field with format support."""
def __init__(
self,
*,
format: str | None = None,
input_formats: list[str] | None = None,
**kwargs: Any
) -> None: ...
class TimeField(Field[datetime.time, datetime.time | str, str, Any]):
"""Time field with format support."""
def __init__(
self,
*,
format: str | None = None,
input_formats: list[str] | None = None,
**kwargs: Any
) -> None: ...
class DurationField(Field[datetime.timedelta, datetime.timedelta | str, str, Any]):
"""Duration field for time intervals."""
passDate/Time Field Parameters:
format: str | None - Output format string (default uses settings)input_formats: list[str] | None - Accepted input formatsdefault_timezone: timezone | None - Default timezone for naive datetimesclass ChoiceField(Field[str, str | int | tuple, str, Any]):
"""Field that validates against a set of choices."""
def __init__(
self,
choices: Iterable[Any],
*,
allow_blank: bool = False,
html_cutoff: int | None = None,
html_cutoff_text: str | None = None,
**kwargs: Any
) -> None: ...
@property
def choices(self) -> dict[Any, str]: ...
@choices.setter
def choices(self, value: Iterable[Any]) -> None: ...
class MultipleChoiceField(ChoiceField, Field[list[Any], list[Any], list[Any], Any]):
"""Field that accepts multiple choices from a set."""
def __init__(
self,
*,
allow_empty: bool = True,
**kwargs: Any
) -> None: ...
class FilePathField(ChoiceField):
"""Field that provides choices from filesystem paths."""
def __init__(
self,
path: str,
*,
match: str | None = None,
recursive: bool = False,
allow_files: bool = True,
allow_folders: bool = False,
**kwargs: Any
) -> None: ...Choice Field Parameters:
choices: Iterable[Any] - Available choices (list, tuple, or dict)allow_blank: bool - Allow empty string selectionallow_empty: bool - Allow empty list (MultipleChoiceField)class FileField(Field[File, File, str | None, Any]):
"""Field for file uploads."""
def __init__(
self,
*,
max_length: int | None = None,
allow_empty_file: bool = False,
use_url: bool = True,
**kwargs: Any
) -> None: ...
class ImageField(FileField):
"""Field for image uploads with validation."""
def __init__(self, *args: Any, **kwargs: Any) -> None: ...File Field Parameters:
max_length: int | None - Maximum filename lengthallow_empty_file: bool - Allow zero-byte filesuse_url: bool - Return file URL in representationclass ListField(Field[list[Any], list[Any], list[Any], Any]):
"""Field for lists of items with child field validation."""
child: Field | None
allow_empty: bool
max_length: int | None
min_length: int | None
def __init__(
self,
*,
child: Field | None = None,
allow_empty: bool = True,
max_length: int | None = None,
min_length: int | None = None,
**kwargs: Any
) -> None: ...
class DictField(Field[dict[Any, Any], dict[Any, Any], dict[Any, Any], Any]):
"""Field for dictionaries with child field validation."""
child: Field | None
allow_empty: bool
def __init__(
self,
*,
child: Field | None = None,
allow_empty: bool = True,
**kwargs: Any
) -> None: ...
class HStoreField(DictField):
"""Field for PostgreSQL HStore data."""
pass
class JSONField(Field[dict[str, Any] | list[dict[str, Any]], Any, str, Any]):
"""Field for JSON data with optional binary encoding."""
def __init__(
self,
*,
binary: bool = False,
encoder: type[json.JSONEncoder] | None = None,
**kwargs: Any
) -> None: ...Container Field Parameters:
child: Field | None - Field type for list/dict valuesallow_empty: bool - Allow empty containersmax_length/min_length: int | None - Size constraintsclass ReadOnlyField(Field[Any, None, Any, Any]):
"""Field that returns attribute value without validation."""
def __init__(self, **kwargs: Any) -> None: ...class HiddenField(Field[Any, None, None, Any]):
"""Field with a value that's not part of user input."""
def __init__(self, *, default: Any = empty, **kwargs: Any) -> None: ...class SerializerMethodField(Field[Any, None, Any, Any]):
"""Field that gets its value by calling a method on the serializer."""
def __init__(
self,
method_name: str | None = None,
**kwargs: Any
) -> None: ...
def bind(self, field_name: str, parent: Any) -> None: ...
def to_representation(self, value: Any) -> Any: ...class ModelField(Field[Any, Any, Any, Any]):
"""Field that wraps a Django model field."""
def __init__(self, model_field: models.Field, **kwargs: Any) -> None: ...class RelatedField(Field[_MT, _DT, _PT, Any]):
"""Base class for fields that represent model relationships."""
queryset: QuerySet[_MT] | Manager[_MT] | None
html_cutoff: int | None
html_cutoff_text: str | None
def __init__(
self,
*,
queryset: QuerySet[_MT] | Manager[_MT] | None = None,
many: bool = False,
allow_empty: bool = True,
**kwargs: Any
) -> None: ...
def get_queryset(self) -> QuerySet[_MT]: ...
def get_choices(self, cutoff: int | None = None) -> dict[Any, str]: ...
def display_value(self, instance: _MT) -> str: ...class StringRelatedField(RelatedField[_MT, _MT, str]):
"""Field that represents relationships using string representation."""
def __init__(self, **kwargs: Any) -> None: ...
def to_representation(self, value: _MT) -> str: ...class PrimaryKeyRelatedField(RelatedField[_MT, _MT, Any]):
"""Field that represents relationships using primary key."""
pk_field: str | None
def __init__(
self,
*,
pk_field: str | None = None,
**kwargs: Any
) -> None: ...
def to_internal_value(self, data: Any) -> _MT: ...
def to_representation(self, value: _MT) -> Any: ...class Hyperlink(str):
"""String subclass for hyperlinked representations."""
@property
def name(self) -> str: ...
obj: Any
is_hyperlink: bool
class HyperlinkedRelatedField(RelatedField[_MT, str, Hyperlink]):
"""Field that represents relationships as hyperlinks."""
view_name: str | None
lookup_field: str
lookup_url_kwarg: str
format: str | None
def __init__(
self,
*,
view_name: str | None = None,
lookup_field: str = 'pk',
lookup_url_kwarg: str | None = None,
format: str | None = None,
**kwargs: Any
) -> None: ...
def get_object(
self,
view_name: str,
view_args: list[Any],
view_kwargs: dict[str, Any]
) -> _MT: ...
def get_url(
self,
obj: Model,
view_name: str,
request: Request,
format: str | None
) -> str | None: ...
class HyperlinkedIdentityField(HyperlinkedRelatedField[_MT, str, Hyperlink]):
"""Hyperlinked field that represents the identity URL of an object."""
def __init__(
self,
*,
view_name: str | None = None,
**kwargs: Any
) -> None: ...class SlugRelatedField(RelatedField[_MT, str, str]):
"""Field that represents relationships using a slug attribute."""
slug_field: str | None
def __init__(
self,
*,
slug_field: str | None = None,
**kwargs: Any
) -> None: ...
def to_internal_value(self, data: str) -> _MT: ...
def to_representation(self, obj: _MT) -> str: ...class ManyRelatedField(Field[Sequence[Any], Sequence[Any], list[Any], Any]):
"""Field for many-to-many relationships."""
child_relation: RelatedField
allow_empty: bool
def __init__(
self,
child_relation: RelatedField | None = None,
*,
allow_empty: bool = True,
**kwargs: Any
) -> None: ...from rest_framework import serializers
class BookSerializer(serializers.Serializer):
"""Demonstrate various field types and configurations."""
# String fields
title = serializers.CharField(max_length=200, min_length=1)
isbn = serializers.RegexField(
regex=r'^(?:\d{10}|\d{13})$',
help_text='10 or 13 digit ISBN'
)
slug = serializers.SlugField(allow_blank=True)
# Numeric fields
pages = serializers.IntegerField(min_value=1, max_value=10000)
price = serializers.DecimalField(
max_digits=8,
decimal_places=2,
min_value=0.01
)
rating = serializers.FloatField(min_value=0.0, max_value=5.0)
# Date fields
published_date = serializers.DateField()
created_at = serializers.DateTimeField(read_only=True)
# Boolean and choice fields
is_available = serializers.BooleanField(default=True)
genre = serializers.ChoiceField(choices=[
('fiction', 'Fiction'),
('non-fiction', 'Non-Fiction'),
('mystery', 'Mystery'),
('sci-fi', 'Science Fiction'),
])
# Container fields
tags = serializers.ListField(
child=serializers.CharField(max_length=50),
allow_empty=True,
max_length=10
)
metadata = serializers.DictField(
child=serializers.CharField(),
allow_empty=True
)class CustomValidationSerializer(serializers.Serializer):
"""Demonstrate field validation and custom fields."""
# Field with custom validators
username = serializers.CharField(
max_length=30,
validators=[
validators.RegexValidator(
regex=r'^[a-zA-Z0-9_]+$',
message='Username can only contain letters, numbers and underscores'
)
]
)
# Email field with custom validation
email = serializers.EmailField()
# Password field (write-only)
password = serializers.CharField(
write_only=True,
min_length=8,
style={'input_type': 'password'}
)
# Confirm password (write-only, not stored)
confirm_password = serializers.CharField(
write_only=True,
style={'input_type': 'password'}
)
# Read-only calculated field
full_name = serializers.SerializerMethodField()
# File upload field
avatar = serializers.ImageField(
allow_empty_file=False,
use_url=True
)
def get_full_name(self, obj: User) -> str:
"""Calculate full name from first and last name."""
return f"{obj.first_name} {obj.last_name}".strip()
def validate_email(self, value: str) -> str:
"""Custom email validation."""
if User.objects.filter(email__iexact=value).exists():
raise serializers.ValidationError('Email already registered')
return value.lower()
def validate(self, data: dict[str, Any]) -> dict[str, Any]:
"""Cross-field validation."""
if data.get('password') != data.get('confirm_password'):
raise serializers.ValidationError('Passwords do not match')
# Remove confirm_password from validated data
data.pop('confirm_password', None)
return dataclass AuthorSerializer(serializers.ModelSerializer):
"""Author serializer with relational fields."""
class Meta:
model = Author
fields = ['id', 'name', 'bio', 'country']
class BookRelationalSerializer(serializers.ModelSerializer):
"""Book serializer demonstrating various relational field types."""
# Foreign key as primary key
author_id = serializers.PrimaryKeyRelatedField(
queryset=Author.objects.all(),
source='author'
)
# Foreign key as string representation
author_name = serializers.StringRelatedField(source='author')
# Foreign key as nested serializer (read-only)
author_detail = AuthorSerializer(source='author', read_only=True)
# Foreign key as hyperlink
author_url = serializers.HyperlinkedRelatedField(
source='author',
view_name='author-detail',
read_only=True
)
# Foreign key using slug field
publisher_slug = serializers.SlugRelatedField(
queryset=Publisher.objects.all(),
slug_field='slug',
source='publisher'
)
# Many-to-many as list of primary keys
category_ids = serializers.PrimaryKeyRelatedField(
queryset=Category.objects.all(),
many=True,
source='categories'
)
# Many-to-many as nested serializers (read-only)
categories = CategorySerializer(many=True, read_only=True)
class Meta:
model = Book
fields = [
'id', 'title', 'author_id', 'author_name', 'author_detail',
'author_url', 'publisher_slug', 'category_ids', 'categories'
]class DynamicFieldsSerializer(serializers.ModelSerializer):
"""Serializer with dynamic field inclusion based on context."""
# Conditional fields based on user permissions
price = serializers.DecimalField(
max_digits=8,
decimal_places=2,
read_only=True
)
# Admin-only fields
internal_notes = serializers.CharField(read_only=True)
cost = serializers.DecimalField(max_digits=8, decimal_places=2, read_only=True)
class Meta:
model = Book
fields = ['id', 'title', 'author', 'price', 'internal_notes', 'cost']
def __init__(self, *args: Any, **kwargs: Any) -> None:
# Get user from context
request = self.context.get('request')
user = getattr(request, 'user', None)
super().__init__(*args, **kwargs)
# Remove price field for anonymous users
if not user or not user.is_authenticated:
self.fields.pop('price', None)
# Remove admin fields for non-staff users
if not user or not user.is_staff:
self.fields.pop('internal_notes', None)
self.fields.pop('cost', None)
# Support dynamic field selection
fields = self.context.get('fields')
if fields is not None:
# Only include specified fields
allowed = set(fields)
existing = set(self.fields)
for field_name in existing - allowed:
self.fields.pop(field_name)class ColorField(serializers.CharField):
"""Custom field for hex color validation."""
def __init__(self, **kwargs: Any) -> None:
kwargs.setdefault('max_length', 7) # #FFFFFF
super().__init__(**kwargs)
def to_internal_value(self, data: str) -> str:
"""Validate and normalize hex color."""
value = super().to_internal_value(data)
# Remove # if present
if value.startswith('#'):
value = value[1:]
# Validate hex format
if not re.match(r'^[0-9A-Fa-f]{6}$', value):
raise serializers.ValidationError('Invalid hex color format')
# Return with # prefix
return f'#{value.upper()}'
def to_representation(self, value: str) -> str:
"""Return hex color with # prefix."""
if value and not value.startswith('#'):
return f'#{value}'
return value
class PhoneNumberField(serializers.CharField):
"""Custom field for phone number validation and formatting."""
def __init__(self, **kwargs: Any) -> None:
kwargs.setdefault('max_length', 20)
super().__init__(**kwargs)
def to_internal_value(self, data: str) -> str:
"""Clean and validate phone number."""
value = super().to_internal_value(data)
# Remove all non-digit characters
digits_only = re.sub(r'[^\d]', '', value)
# Validate US phone number (10 digits)
if len(digits_only) == 10:
return f'({digits_only[:3]}) {digits_only[3:6]}-{digits_only[6:]}'
elif len(digits_only) == 11 and digits_only[0] == '1':
# Handle +1 country code
return f'+1 ({digits_only[1:4]}) {digits_only[4:7]}-{digits_only[7:]}'
else:
raise serializers.ValidationError('Invalid phone number format')
def to_representation(self, value: str) -> str:
"""Return formatted phone number."""
return value
class Base64ImageField(serializers.ImageField):
"""Custom field that accepts base64 encoded images."""
def to_internal_value(self, data: str) -> File:
"""Convert base64 string to image file."""
if isinstance(data, str) and data.startswith('data:image'):
# Parse data URL: data:image/jpeg;base64,/9j/4AAQ...
try:
format_match = re.match(r'data:image/(\w+);base64,(.+)', data)
if not format_match:
raise serializers.ValidationError('Invalid image data format')
image_format = format_match.group(1).lower()
image_data = format_match.group(2)
# Decode base64
decoded_data = base64.b64decode(image_data)
# Create file-like object
image_file = ContentFile(decoded_data, name=f'image.{image_format}')
return super().to_internal_value(image_file)
except (ValueError, TypeError) as e:
raise serializers.ValidationError(f'Invalid base64 image: {e}')
# Fall back to regular image field handling
return super().to_internal_value(data)def is_simple_callable(obj: Callable) -> bool:
"""Check if object is a simple callable (not a class)."""
...
def get_attribute(instance: Any, attrs: list[str] | None) -> Any:
"""
Get nested attribute from instance using dot notation.
Args:
instance: Object to get attribute from
attrs: List of attribute names for nested access
Returns:
Any: Attribute value
"""
...
def to_choices_dict(choices: Iterable[Any]) -> dict[Any, str]:
"""Convert choices iterable to dictionary format."""
...
def flatten_choices_dict(choices: dict[Any, Any]) -> dict[Any, str]:
"""Flatten nested choices dictionary."""
...
def iter_options(
grouped_choices: dict[Any, Any],
cutoff: int | None = None,
cutoff_text: str | None = None
) -> Generator[Any, None, None]:
"""Iterate over grouped choices with optional cutoff."""
...
def get_error_detail(exc_info: Any) -> Any:
"""Extract error detail from exception info."""
...class CreateOnlyDefault:
"""Default value that only applies during creation."""
def __init__(self, default: Any) -> None: ...
def set_context(self, serializer_field: Field) -> None: ...
def __call__(self) -> Any: ...
class CurrentUserDefault:
"""Default value that returns the current user."""
requires_context: bool # True
def set_context(self, serializer_field: Field) -> None: ...
def __call__(self) -> Any: ...# Empty value sentinel
empty: Final[Any]
# Regular expression type
REGEX_TYPE: type[Pattern[str]]
# Error messages
MISSING_ERROR_MESSAGE: str
INVALID_ERROR_MESSAGE: str
REQUIRED_ERROR_MESSAGE: str
ALLOW_NULL_ERROR_MESSAGE: str
NOT_A_DICT_ERROR_MESSAGE: str
NOT_A_LIST_ERROR_MESSAGE: str
# ... and many more error message constantsThis comprehensive field and relations system provides type-safe data validation and serialization with full mypy support, enabling confident implementation of robust data handling patterns in Django REST Framework applications.
Install with Tessl CLI
npx tessl i tessl/pypi-djangorestframework-stubs