CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-wtforms

Form validation and rendering for Python web development.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

validation.mddocs/

Validation

Robust validation framework with built-in validators for common use cases and support for custom validation logic. The validation system provides data requirements, length constraints, format validation, cross-field comparisons, and extensible custom validation patterns.

Capabilities

Validation Exceptions

Core exception classes for validation error handling.

class ValidationError(ValueError):
    """
    Exception raised when validation fails.
    
    Parameters:
    - message: Error message string
    """
    def __init__(self, message=""): ...

class StopValidation(Exception):
    """
    Exception that stops the validation chain.
    Used by validators like Optional to halt further validation.
    
    Parameters:
    - message: Optional error message
    """
    def __init__(self, message=""): ...

Data Requirement Validators

Validators that check for presence and validity of input data.

class DataRequired:
    """
    Validates that field contains truthy data.
    Fails on empty strings, empty lists, None, etc.
    
    Parameters:
    - message: Custom error message
    """
    def __init__(self, message=None): ...
    def __call__(self, form, field): ...

class InputRequired:
    """
    Validates that form input was provided (even if empty).
    Unlike DataRequired, accepts empty strings as valid input.
    Sets 'required' flag on field.
    
    Parameters:
    - message: Custom error message
    """
    def __init__(self, message=None): ...
    def __call__(self, form, field): ...

class Optional:
    """
    Allows empty input and stops validation chain.
    If field is empty, no further validators are called.
    Sets 'optional' flag on field.
    
    Parameters:
    - strip_whitespace: Whether to strip whitespace before checking (default: True)
    """
    def __init__(self, strip_whitespace=True): ...
    def __call__(self, form, field): ...

Length and Range Validators

Validators for constraining input length and numeric ranges.

class Length:
    """
    Validates string length within specified bounds.
    Sets 'minlength' and 'maxlength' flags on field.
    
    Parameters:
    - min: Minimum length (-1 for no minimum, default: -1)
    - max: Maximum length (-1 for no maximum, default: -1)  
    - message: Custom error message
    """
    def __init__(self, min=-1, max=-1, message=None): ...
    def __call__(self, form, field): ...

class NumberRange:
    """
    Validates numeric value within specified range.
    Sets 'min' and 'max' flags on field.
    
    Parameters:
    - min: Minimum value (None for no minimum)
    - max: Maximum value (None for no maximum)
    - message: Custom error message
    """
    def __init__(self, min=None, max=None, message=None): ...
    def __call__(self, form, field): ...

Comparison and Equality Validators

Validators for comparing field values.

class EqualTo:
    """
    Compares field value to another field in the same form.
    Commonly used for password confirmation fields.
    
    Parameters:
    - fieldname: Name of field to compare against
    - message: Custom error message
    """
    def __init__(self, fieldname, message=None): ...
    def __call__(self, form, field): ...

Pattern and Format Validators

Validators for specific data formats and patterns.

class Regexp:
    """
    Validates field data against regular expression pattern.
    
    Parameters:
    - regex: Regular expression pattern (string or compiled regex)
    - flags: Regex flags (default: 0)
    - message: Custom error message
    """
    def __init__(self, regex, flags=0, message=None): ...
    def __call__(self, form, field): ...

class Email:
    """
    Validates email address format.
    Requires 'email_validator' package for full validation.
    
    Parameters:
    - message: Custom error message
    - granular_message: Whether to show specific validation errors
    - check_deliverability: Whether to check if domain accepts email
    - allow_smtputf8: Whether to allow international domains
    - allow_empty_local: Whether to allow empty local part
    """
    def __init__(self, message=None, granular_message=False, 
                 check_deliverability=True, allow_smtputf8=True, 
                 allow_empty_local=False): ...
    def __call__(self, form, field): ...

class URL:
    """
    Validates URL format.
    
    Parameters:
    - require_tld: Whether to require top-level domain (default: True)
    - message: Custom error message
    """
    def __init__(self, require_tld=True, message=None): ...
    def __call__(self, form, field): ...

class IPAddress:
    """
    Validates IP address format.
    
    Parameters:
    - ipv4: Whether to allow IPv4 addresses (default: True)
    - ipv6: Whether to allow IPv6 addresses (default: False)
    - message: Custom error message
    """
    def __init__(self, ipv4=True, ipv6=False, message=None): ...
    def __call__(self, form, field): ...

class MacAddress:
    """
    Validates MAC address format.
    
    Parameters:
    - message: Custom error message
    """
    def __init__(self, message=None): ...
    def __call__(self, form, field): ...

class UUID:
    """
    Validates UUID format.
    
    Parameters:
    - message: Custom error message
    """
    def __init__(self, message=None): ...
    def __call__(self, form, field): ...

Choice Validators

Validators for constraining values to specific choices.

class AnyOf:
    """
    Validates that field value is in allowed list.
    
    Parameters:
    - values: Iterable of allowed values
    - message: Custom error message
    - values_formatter: Function to format values in error message
    """
    def __init__(self, values, message=None, values_formatter=None): ...
    def __call__(self, form, field): ...

class NoneOf:
    """
    Validates that field value is NOT in disallowed list.
    
    Parameters:
    - values: Iterable of disallowed values
    - message: Custom error message
    - values_formatter: Function to format values in error message
    """
    def __init__(self, values, message=None, values_formatter=None): ...
    def __call__(self, form, field): ...

State Validators

Validators that set field state or behavior.

class ReadOnly:
    """
    Marks field as read-only for display purposes.
    Sets 'readonly' flag on field.
    
    Parameters:
    - message: Custom error message if field is modified
    """
    def __init__(self, message=None): ...
    def __call__(self, form, field): ...

class Disabled:
    """
    Marks field as disabled.
    Sets 'disabled' flag on field.
    
    Parameters:
    - message: Custom error message if field is modified
    """
    def __init__(self, message=None): ...
    def __call__(self, form, field): ...

Validator Function Aliases

WTForms provides lowercase function aliases for all validator classes for convenience.

# Lowercase aliases for validator classes
data_required = DataRequired
input_required = InputRequired
optional = Optional
length = Length
number_range = NumberRange
equal_to = EqualTo
regexp = Regexp
email = Email
url = URL
ip_address = IPAddress
mac_address = MacAddress
any_of = AnyOf
none_of = NoneOf
readonly = ReadOnly
disabled = Disabled

Validation Usage Examples

Basic Validation

from wtforms import Form, StringField, IntegerField, validators

class UserForm(Form):
    username = StringField('Username', [
        validators.DataRequired(message="Username is required"),
        validators.Length(min=4, max=25, message="Username must be 4-25 characters")
    ])
    
    email = StringField('Email', [
        validators.DataRequired(),
        validators.Email(message="Invalid email address")
    ])
    
    age = IntegerField('Age', [
        validators.NumberRange(min=13, max=120, message="Age must be 13-120")
    ])
    
    bio = StringField('Bio', [
        validators.Optional(),  # Field is optional
        validators.Length(max=500)  # But if provided, max 500 chars
    ])

# Validation
form = UserForm(formdata=request.form)
if form.validate():
    # All fields passed validation
    process_user_data(form.data)
else:
    # Display errors
    for field, errors in form.errors.items():
        for error in errors:
            print(f"{field}: {error}")

Password Confirmation

class RegistrationForm(Form):
    password = StringField('Password', [
        validators.DataRequired(),
        validators.Length(min=8, message="Password must be at least 8 characters")
    ])
    
    confirm_password = StringField('Confirm Password', [
        validators.DataRequired(),
        validators.EqualTo('password', message='Passwords must match')
    ])

Email Validation with Options

class ContactForm(Form):
    # Basic email validation
    email = StringField('Email', [validators.Email()])
    
    # Email with custom message and options
    business_email = StringField('Business Email', [
        validators.Email(
            message="Please enter a valid business email",
            check_deliverability=True,  # Verify domain accepts email
            granular_message=True  # Show specific validation errors
        )
    ])

Pattern Validation

class ProductForm(Form):
    # SKU must be format: ABC-1234
    sku = StringField('SKU', [
        validators.Regexp(
            r'^[A-Z]{3}-\d{4}$',
            message="SKU must be format ABC-1234"
        )
    ])
    
    # Phone number validation
    phone = StringField('Phone', [
        validators.Regexp(
            r'^\+?1?-?\(?(\d{3})\)?-?(\d{3})-?(\d{4})$',
            message="Invalid phone number format"
        )
    ])

Choice Validation

class SurveyForm(Form):
    rating = IntegerField('Rating', [
        validators.AnyOf([1, 2, 3, 4, 5], message="Rating must be 1-5")
    ])
    
    language = StringField('Language', [
        validators.AnyOf(['en', 'es', 'fr'], message="Unsupported language")
    ])
    
    # Forbidden words
    comment = StringField('Comment', [
        validators.NoneOf(['spam', 'test'], message="Comment contains forbidden words")
    ])

Network Address Validation

class ServerForm(Form):
    # IPv4 only
    ipv4_address = StringField('IPv4 Address', [
        validators.IPAddress(ipv4=True, ipv6=False)
    ])
    
    # IPv6 only  
    ipv6_address = StringField('IPv6 Address', [
        validators.IPAddress(ipv4=False, ipv6=True)
    ])
    
    # Either IPv4 or IPv6
    ip_address = StringField('IP Address', [
        validators.IPAddress(ipv4=True, ipv6=True)
    ])
    
    mac_address = StringField('MAC Address', [
        validators.MacAddress()
    ])
    
    server_id = StringField('Server ID', [
        validators.UUID(message="Must be valid UUID")
    ])

Custom Validators

def validate_username_available(form, field):
    """Custom validator function."""
    if User.query.filter_by(username=field.data).first():
        raise ValidationError('Username already taken.')

class UsernameAvailable:
    """Custom validator class."""
    def __init__(self, message=None):
        self.message = message or 'Username already taken.'
    
    def __call__(self, form, field):
        if User.query.filter_by(username=field.data).first():
            raise ValidationError(self.message)

class RegistrationForm(Form):
    username = StringField('Username', [
        validators.DataRequired(),
        validate_username_available,  # Function validator
        UsernameAvailable()  # Class validator
    ])

Conditional Validation

class ShippingForm(Form):
    same_as_billing = BooleanField('Same as billing address')
    shipping_address = StringField('Shipping Address')
    
    def validate_shipping_address(self, field):
        """Custom validation method - only required if different from billing."""
        if not self.same_as_billing.data and not field.data:
            raise ValidationError('Shipping address is required.')

Validation with Filters

from wtforms.filters import strip_filter

def uppercase_filter(data):
    """Custom filter to uppercase data."""
    return data.upper() if data else data

class ProductForm(Form):
    name = StringField('Product Name', 
        filters=[strip_filter],  # Remove leading/trailing whitespace
        validators=[validators.DataRequired()]
    )
    
    code = StringField('Product Code',
        filters=[strip_filter, uppercase_filter],  # Strip and uppercase
        validators=[validators.DataRequired()]
    )

# Usage
form = ProductForm(formdata={'name': '  Widget  ', 'code': '  abc123  '})
form.process()
print(form.name.data)  # "Widget" (stripped)
print(form.code.data)  # "ABC123" (stripped and uppercased)

Validation Error Handling

form = MyForm(formdata=request.form)

try:
    if form.validate():
        # Process valid form
        save_form_data(form.data)
    else:
        # Handle validation errors
        for field_name, errors in form.errors.items():
            for error in errors:
                flash(f"Error in {field_name}: {error}", 'error')
                
except ValidationError as e:
    # Handle specific validation exception
    flash(f"Validation failed: {e}", 'error')
    
# Access individual field errors
if form.email.errors:
    email_errors = form.email.errors
    print(f"Email validation failed: {', '.join(email_errors)}")

Dynamic Validation

class DynamicForm(Form):
    email = StringField('Email')
    
    def __init__(self, require_email=False, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        # Add validators dynamically
        if require_email:
            self.email.validators = [
                validators.DataRequired(),
                validators.Email()
            ]
        else:
            self.email.validators = [validators.Optional()]

# Usage
form = DynamicForm(require_email=True, formdata=request.form)

Validation with Extra Validators

def validate_during_business_hours(form, field):
    """Validator that only applies during business hours."""
    if datetime.now().hour < 9 or datetime.now().hour > 17:
        raise ValidationError('This form can only be submitted during business hours.')

class ContactForm(Form):
    message = StringField('Message', [validators.DataRequired()])

# Add extra validation at runtime
form = ContactForm(formdata=request.form)
extra_validators = {
    'message': [validate_during_business_hours]
}

if form.validate(extra_validators=extra_validators):
    # Process form
    pass

Install with Tessl CLI

npx tessl i tessl/pypi-wtforms

docs

csrf.md

fields.md

forms.md

i18n.md

index.md

validation.md

widgets.md

tile.json