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)

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.

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])

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 None and log a warning

  • Evaluated 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)
    )

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)