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.
All form types must implement the FormTypeInterface abstract base class.
Methods
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
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'
}
Dictionary of default form options.
Base class for field types that provides common functionality.
Properties
Configuration options for the field type.
List of validation errors for the field.
The current value of the field.
Methods
Transforms the form value to a model value.
The raw form value to transform.
The transformed value ready for the model.
Transforms the model value to a view value.
The model value to transform.
The transformed value ready for display.
get_block_prefix
Returns the block prefix for rendering.
The block prefix identifier (e.g., “text”, “email”, “choice”).
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'
}
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}
})
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:
The label text displayed for the field.
Whether the field is required.
HTML attributes to add to the field (e.g., {'class': 'custom-input', 'placeholder': 'Enter value'}).
HTML attributes for the field row wrapper.
Help text displayed below the field.
Whether the field is disabled.
Default data for the field.
See Also