Skip to main content
Form types define the structure and fields of your forms. By creating custom form type classes, you can encapsulate form logic and reuse form definitions across your application.

FormTypeInterface

All form types must implement the FormTypeInterface abstract base class.

Methods

build_form

Defines the fields and structure of the form.
from framefox.core.form import FormTypeInterface, FormBuilder

class MyFormType(FormTypeInterface):
    def build_form(self, builder: FormBuilder) -> None:
        # Add fields here
        pass
builder
FormBuilder
required
The form builder instance used to add fields to the form.

get_options

Returns default options for the form (optional).
def get_options(self) -> Dict[str, Any]:
    return {
        'action': '/submit',
        'method': 'POST'
    }
options
Dict[str, Any]
Dictionary of default form options.

AbstractFormType

Base class for field types that provides common functionality.

Properties

options
Dict[str, Any]
Configuration options for the field type.
name
str
The field name.
errors
List[str]
List of validation errors for the field.
value
Any
The current value of the field.

Methods

transform_to_model

Transforms the form value to a model value.
value
Any
required
The raw form value to transform.
transformed
Any
The transformed value ready for the model.

transform_to_view

Transforms the model value to a view value.
value
Any
required
The model value to transform.
transformed
Any
The transformed value ready for display.

get_block_prefix

Returns the block prefix for rendering.
prefix
str
The block prefix identifier (e.g., “text”, “email”, “choice”).

Creating Custom Form Types

Basic Form Type

from framefox.core.form import FormTypeInterface, FormBuilder
from framefox.core.form.type import TextType, EmailType, PasswordType

class UserRegistrationForm(FormTypeInterface):
    """Form for user registration."""
    
    def build_form(self, builder: FormBuilder) -> None:
        builder.add('username', TextType, {
            'label': 'Username',
            'required': True,
            'attr': {'placeholder': 'Enter username'}
        })
        
        builder.add('email', EmailType, {
            'label': 'Email Address',
            'required': True,
            'attr': {'placeholder': '[email protected]'}
        })
        
        builder.add('password', PasswordType, {
            'label': 'Password',
            'required': True,
            'attr': {'minlength': 8}
        })
    
    def get_options(self) -> Dict[str, Any]:
        return {
            'action': '/register',
            'method': 'POST'
        }

Form Type with Choices

from framefox.core.form import FormTypeInterface, FormBuilder
from framefox.core.form.type import TextType, EmailType, ChoiceType

class ContactForm(FormTypeInterface):
    def build_form(self, builder: FormBuilder) -> None:
        builder.add('name', TextType, {
            'label': 'Your Name',
            'required': True
        })
        
        builder.add('email', EmailType, {
            'label': 'Your Email',
            'required': True
        })
        
        builder.add('subject', ChoiceType, {
            'label': 'Subject',
            'required': True,
            'choices': {
                'general': 'General Inquiry',
                'support': 'Technical Support',
                'billing': 'Billing Question',
                'other': 'Other'
            }
        })
        
        builder.add('message', TextType, {
            'label': 'Message',
            'required': True,
            'attr': {'rows': 5}
        })

Form Type with Entity Relations

from framefox.core.form import FormTypeInterface, FormBuilder
from framefox.core.form.type import TextType, EntityType, ChoiceType
from myapp.models import Category

class ArticleForm(FormTypeInterface):
    def build_form(self, builder: FormBuilder) -> None:
        builder.add('title', TextType, {
            'label': 'Article Title',
            'required': True
        })
        
        builder.add('category', EntityType, {
            'label': 'Category',
            'class': Category,
            'choice_label': 'name',
            'required': True
        })
        
        builder.add('status', ChoiceType, {
            'label': 'Status',
            'choices': {
                'draft': 'Draft',
                'published': 'Published',
                'archived': 'Archived'
            },
            'required': True
        })
        
        builder.add('content', TextType, {
            'label': 'Content',
            'required': True
        })

Built-in Field Types

Framefox provides several built-in field types:

TextType

Basic text input field.
builder.add('name', TextType, {
    'label': 'Name',
    'required': True,
    'attr': {'placeholder': 'Enter your name'}
})

EmailType

Email input with validation.
builder.add('email', EmailType, {
    'label': 'Email',
    'required': True
})

PasswordType

Password input field.
builder.add('password', PasswordType, {
    'label': 'Password',
    'required': True,
    'attr': {'minlength': 8}
})

NumberType

Numeric input field.
builder.add('age', NumberType, {
    'label': 'Age',
    'attr': {'min': 0, 'max': 150}
})

ChoiceType

Select dropdown or radio/checkbox inputs.
# Dropdown
builder.add('country', ChoiceType, {
    'label': 'Country',
    'choices': {
        'us': 'United States',
        'uk': 'United Kingdom',
        'ca': 'Canada'
    }
})

# Radio buttons
builder.add('gender', ChoiceType, {
    'label': 'Gender',
    'expanded': True,
    'choices': {
        'm': 'Male',
        'f': 'Female',
        'o': 'Other'
    }
})

# Multiple selection
builder.add('interests', ChoiceType, {
    'label': 'Interests',
    'multiple': True,
    'expanded': True,
    'choices': {
        'sports': 'Sports',
        'music': 'Music',
        'reading': 'Reading'
    }
})

CheckboxType

Single checkbox for boolean values.
builder.add('agree_terms', CheckboxType, {
    'label': 'I agree to the terms and conditions',
    'required': True
})

TextareaType

Multi-line text input.
builder.add('description', TextareaType, {
    'label': 'Description',
    'attr': {'rows': 5, 'cols': 40}
})

DateTimeType

Date and time input.
builder.add('event_date', DateTimeType, {
    'label': 'Event Date',
    'required': True
})

FileType

File upload field.
builder.add('avatar', FileType, {
    'label': 'Profile Picture',
    'upload_dir': 'uploads/avatars',
    'allowed_extensions': ['jpg', 'png', 'gif']
})

# Multiple files
builder.add('attachments', FileType, {
    'label': 'Attachments',
    'multiple': True,
    'upload_dir': 'uploads/documents'
})

EntityType

Select from database entities.
from myapp.models import User

builder.add('author', EntityType, {
    'label': 'Author',
    'class': User,
    'choice_label': 'username',
    'required': True
})

CollectionType

Repeat a form type multiple times.
builder.add('addresses', CollectionType, {
    'label': 'Addresses',
    'entry_type': AddressType,
    'allow_add': True,
    'allow_delete': True
})

Creating Custom Field Types

from typing import Any
from framefox.core.form.type import AbstractFormType

class PhoneType(AbstractFormType):
    """Custom field type for phone numbers."""
    
    def transform_to_model(self, value: Any) -> str:
        """Remove formatting from phone number."""
        if value is None:
            return ""
        # Remove non-numeric characters
        return ''.join(filter(str.isdigit, str(value)))
    
    def transform_to_view(self, value: Any) -> str:
        """Format phone number for display."""
        if value is None:
            return ""
        digits = str(value)
        if len(digits) == 10:
            return f"({digits[:3]}) {digits[3:6]}-{digits[6:]}"
        return digits
    
    def get_block_prefix(self) -> str:
        return "phone"

# Usage
builder.add('phone', PhoneType, {
    'label': 'Phone Number',
    'attr': {'placeholder': '(555) 123-4567'}
})

Field Options

Common options available for all field types:
label
str
The label text displayed for the field.
required
bool
default:"false"
Whether the field is required.
attr
Dict[str, Any]
HTML attributes to add to the field (e.g., {'class': 'custom-input', 'placeholder': 'Enter value'}).
row_attr
Dict[str, Any]
HTML attributes for the field row wrapper.
help
str
Help text displayed below the field.
disabled
bool
default:"false"
Whether the field is disabled.
data
Any
Default data for the field.

See Also

Build docs developers (and LLMs) love