Field Decorator
Overview
The field() decorator is the core component for defining individual input fields in the
Declarative API. It provides comprehensive configuration options for field validation,
filtering, and processing.
- flask_inputfilter.declarative.field.field(*, required=False, default=None, fallback=None, filters=None, validators=None, steps=None, external_api=None, copy=None, computed=None, input_filter=None)
Create a field descriptor for declarative field definition.
This function creates a FieldDescriptor that can be used as a class attribute to define input filter fields declaratively.
Parameters:
required (bool): Whether the field is required. Default: False.
default (Any): The default value of the field. Default: None.
fallback (Any): The fallback value of the field, if validations fail or field is None, although it is required. Default: None.
filters (Optional[list[BaseFilter]]): The filters to apply to the field value. Default: None.
validators (Optional[list[BaseValidator]]): The validators to apply to the field value. Default: None.
steps (Optional[list[Union[BaseFilter, BaseValidator]]]): Allows to apply multiple filters and validators in a specific order. Default: None.
external_api (Optional[ExternalApiConfig]): Configuration for an external API call. Default: None.
copy (Optional[str]): The name of the field to copy the value from. Default: None.
computed (Optional[Callable[[dict[str, Any]], Any]]): A callable that computes the field value from validated data. Default: None.
input_filter (Optional[type]): An InputFilter class to use for nested validation. When specified, the field value (must be a dict) will be validated against the nested InputFilter’s rules. Default: None.
Returns:
A field descriptor configured with the given parameters.
Example:
from flask_inputfilter import InputFilter from flask_inputfilter.declarative import field from flask_inputfilter.validators import IsStringValidator class UserInputFilter(InputFilter): name: str = field(required=True, validators=[IsStringValidator()]) age: int = field(required=True, default=18)
- Parameters:
required (bool)
default (Any)
fallback (Any)
filters (Optional[list[BaseFilter]])
validators (Optional[list[BaseValidator]])
steps (Optional[list[Union[BaseFilter, BaseValidator]]])
external_api (Optional[ExternalApiConfig])
copy (Optional[str])
computed (Optional[Any])
input_filter (Optional[type])
- Return type:
FieldDescriptor
Basic Usage
The simplest field definition:
from flask_inputfilter import InputFilter
from flask_inputfilter.declarative import field
class SimpleFilter(InputFilter):
name = field() # Optional field with no validation
Required Fields
class UserFilter(InputFilter):
username = field(required=True)
email = field(required=True)
Field Configuration Options
The field() decorator accepts the following parameters:
required
Type: bool
Default: False
Specifies whether the field must be present in the input data.
class RegistrationFilter(InputFilter):
username = field(required=True) # Must be provided
nickname = field(required=False) # Optional
Examples:
# Required field - will raise ValidationError if missing
email = field(required=True)
# Optional field - won't raise error if missing
phone = field(required=False)
default
Type: Any
Default: None
Provides a default value when the field is not present in the input data.
Only applies to optional fields (required=False).
class UserFilter(InputFilter):
name = field(required=True)
role = field(required=False, default="user")
active = field(required=False, default=True)
tags = field(required=False, default=[])
Important: Use default=[] carefully with mutable objects. Consider using a factory function:
from flask_inputfilter.filters import DefaultValueFilter
class SafeDefaultFilter(InputFilter):
# Safe for mutable defaults
tags = field(required=False, filters=[DefaultValueFilter(lambda: [])])
fallback
Type: Any
Default: None
Specifies a value to use when validation fails or when processing encounters errors.
Unlike default, fallback applies even when the field is present but invalid.
from flask_inputfilter.validators import IsIntegerValidator
class RobustFilter(InputFilter):
age = field(
required=False,
validators=[IsIntegerValidator()],
fallback=0 # Use 0 if validation fails
)
priority = field(
required=True,
validators=[IsIntegerValidator()],
fallback=1 # Use 1 if provided value is invalid
)
filters
Type: list[BaseFilter]
Default: []
List of filters to apply to the field value. Filters are applied in the order specified and transform the input data before validation.
from flask_inputfilter.filters import StringTrimFilter, ToLowerFilter, ToIntegerFilter
class FilteredInputFilter(InputFilter):
username = field(
required=True,
filters=[StringTrimFilter(), ToLowerFilter()]
)
age = field(
required=False,
filters=[ToIntegerFilter()]
)
Common Filters:
from flask_inputfilter.filters import (
StringTrimFilter, # Remove leading/trailing whitespace
ToLowerFilter, # Convert to lowercase
ToUpperFilter, # Convert to uppercase
ToIntegerFilter, # Convert to integer
ToFloatFilter, # Convert to float
ToBooleanFilter, # Convert to boolean
ToNullFilter, # Convert empty strings to None
RemoveFilter, # Remove specific characters
ReplaceFilter, # Replace characters/patterns
)
class ComprehensiveFilter(InputFilter):
email = field(filters=[StringTrimFilter(), ToLowerFilter()])
price = field(filters=[ToFloatFilter()])
active = field(filters=[ToBooleanFilter()])
validators
Type: list[BaseValidator]
Default: []
List of validators to apply to the field value. Validators check the processed value and raise validation errors if the value doesn’t meet the criteria.
from flask_inputfilter.validators import (
IsStringValidator, LengthValidator, EmailValidator, RegexValidator
)
class ValidatedFilter(InputFilter):
username = field(
required=True,
validators=[
IsStringValidator(),
LengthValidator(min_length=3, max_length=20)
]
)
email = field(
required=True,
validators=[IsStringValidator(), EmailValidator()]
)
Common Validators:
from flask_inputfilter.validators import (
IsStringValidator, # Must be a string
IsIntegerValidator, # Must be an integer
IsFloatValidator, # Must be a float
IsBooleanValidator, # Must be a boolean
IsListValidator, # Must be a list
IsDictValidator, # Must be a dictionary
LengthValidator, # String/list length validation
RangeValidator, # Numeric range validation
EmailValidator, # Email format validation
UrlValidator, # URL format validation
RegexValidator, # Custom regex validation
InValidator, # Value must be in allowed list
NotInValidator, # Value must not be in forbidden list
)
Validator Examples:
class DetailedValidationFilter(InputFilter):
# String validation with length constraints
password = field(
required=True,
validators=[
IsStringValidator(),
LengthValidator(min_length=8, max_length=128)
]
)
# Numeric validation with range
age = field(
required=True,
validators=[
IsIntegerValidator(),
RangeValidator(min_value=13, max_value=120)
]
)
# Choice validation
status = field(
required=True,
validators=[
IsStringValidator(),
InValidator(['active', 'inactive', 'pending'])
]
)
# Custom regex validation
phone = field(
required=False,
validators=[
IsStringValidator(),
RegexValidator(r'^\+?1?\d{9,15}$', message="Invalid phone number format")
]
)
steps
Type: list
Default: []
Defines a sequence of processing steps (filters and validators) that are applied in order. This allows for fine-grained control over the processing pipeline.
from flask_inputfilter.filters import StringTrimFilter, ToIntegerFilter
from flask_inputfilter.validators import IsStringValidator, IsIntegerValidator
class SteppedFilter(InputFilter):
numeric_input = field(
required=True,
steps=[
StringTrimFilter(), # Step 1: Remove whitespace
IsStringValidator(), # Step 2: Validate it's a string
ToIntegerFilter(), # Step 3: Convert to integer
IsIntegerValidator(), # Step 4: Validate it's an integer
RangeValidator(min_value=0) # Step 5: Validate range
]
)
external_api
Type: ExternalApiConfig
Default: None
Configuration for fetching field values from external APIs. Useful for data enrichment or validation against external services.
from flask_inputfilter.models import ExternalApiConfig
class ExternalDataFilter(InputFilter):
user_id = field(required=True, validators=[IsIntegerValidator()])
# Fetch user details from external API
user_profile = field(
required=False,
external_api=ExternalApiConfig(
url="https://api.example.com/users/{user_id}",
method="GET",
headers={"Authorization": "Bearer token"}
)
)
For detailed information, see ExternalApi.
copy
Type: str
Default: None
Copy the value from another field. The copied value can then be filtered and validated independently.
class CopyFieldFilter(InputFilter):
email = field(required=True, validators=[EmailValidator()])
# Copy email value for confirmation
email_confirmation = field(
required=True,
copy="email",
validators=[EmailValidator()]
)
# Processing happens after copying
normalized_email = field(
required=False,
copy="email",
filters=[StringTrimFilter(), ToLowerFilter()]
)
For detailed information, see Copy.
computed
Type: Callable[[dict[str, Any]], Any]
Default: None
Define a read-only field that is automatically calculated from other fields. The provided callable receives the current data dictionary and should return the computed value.
Computed fields are:
Read-only: Input values are ignored
Non-blocking: Errors let the field stay on
Noneand log a warningEvaluated during validation: Have access to all previously processed fields
class OrderInputFilter(InputFilter):
quantity: int = field(required=True)
price: float = field(required=True)
# Computed field using lambda
total: float = field(
computed=lambda data: data['quantity'] * data['price']
)
# Using named function for complex calculations
def calculate_tax(data):
subtotal = data.get('subtotal', 0)
tax_rate = data.get('tax_rate', 0.19)
return subtotal * tax_rate
class InvoiceInputFilter(InputFilter):
subtotal: float = field(required=True)
tax_rate: float = field(default=0.19)
tax: float = field(computed=calculate_tax)
total: float = field(
required=True,
computed=lambda data: data['subtotal'] + data.get('tax', 0)
)
input_filter
Type: type
Default: None
Specify an InputFilter class to use for nested validation. When this parameter is provided, the field value must be a dictionary and will be validated against the nested InputFilter’s rules.
This allows you to compose complex validation structures by nesting InputFilters within each other, enabling validation of nested objects and hierarchical data structures.
Key Features:
Validates nested dictionary structures
Applies all filters and validators from the nested InputFilter
Supports multiple levels of nesting
Provides clear error messages with field context
from flask_inputfilter import InputFilter
from flask_inputfilter.declarative import field
from flask_inputfilter.validators import IsIntegerValidator, IsStringValidator
class UserInputFilter(InputFilter):
id: int = field(required=True, validators=[IsIntegerValidator()])
name: str = field(required=True, validators=[IsStringValidator()])
email: str = field(required=True, validators=[IsStringValidator()])
class OrderInputFilter(InputFilter):
quantity: int = field(required=True, validators=[IsIntegerValidator()])
user: dict = field(required=True, input_filter=UserInputFilter)
Error Handling:
If nested validation fails, the error will include context about which field failed:
# If user.name is missing:
# ValidationError: {'user': "Nested validation failed for field 'user': {'name': \"Field 'name' is required.\"}"}
Important Notes:
The field value must be a dictionary, otherwise a validation error is raised
If the field is optional (
required=False) and the value isNone, nested validation is skippedAll filters and validators from the nested InputFilter are applied
The nested InputFilter can also have its own nested fields, allowing unlimited nesting depth
Advanced Field Patterns
Multi-Type Fields
Fields that can accept multiple types:
from flask_inputfilter.validators import OrValidator
class FlexibleTypeFilter(InputFilter):
# Can be either string or integer
identifier = field(
required=True,
validators=[
OrValidator([
IsStringValidator(),
IsIntegerValidator()
])
]
)
Custom Field Processing
Create reusable field configurations:
# Define reusable field types
def email_field(required=True):
return field(
required=required,
filters=[StringTrimFilter(), ToLowerFilter()],
validators=[IsStringValidator(), EmailValidator()]
)
def username_field(min_length=3, max_length=20):
return field(
required=True,
filters=[StringTrimFilter()],
validators=[
IsStringValidator(),
LengthValidator(min_length=min_length, max_length=max_length),
RegexValidator(r'^[a-zA-Z0-9_]+$', message="Only letters, numbers, and underscores allowed")
]
)
class UserFilter(InputFilter):
username = username_field()
email = email_field()
backup_email = email_field(required=False)