Working with Forms
Rick provides powerful form handling and request validation capabilities through two main classes:
- Form - Full-featured forms with fieldsets, controls, and HTML form support
- RequestRecord - Lightweight request validation for APIs and data processing
Overview
Form
Form is a generic form component designed for building and validating HTML forms. It supports:
- Field declaration with validation rules
- Field groups (fieldsets) for organizing complex forms
- Custom validation methods
- Form controls (buttons, etc.)
- HTTP method and action URL configuration
- Agnostic design (no rendering - bring your own template engine)
RequestRecord
RequestRecord is a lightweight class focused on structured request data validation. It's ideal for:
- API request validation
- Data processing pipelines
- Nested data structures
- Object binding
- Microservices and REST APIs
Quick Start
Simple Form
from rick.form import Form
# Create form
form = Form()
form.field("text", "username", "Username", validators="required|alphanum|minlen:3")
form.field("email", "email", "Email", validators="required|email")
form.field("password", "password", "Password", validators="required|minlen:8")
# Validate data
data = {
"username": "alice",
"email": "alice@example.com",
"password": "SecurePass123"
}
if form.is_valid(data):
print(f"Welcome {form.get('username')}!")
else:
print(f"Errors: {form.get_errors()}")
Simple RequestRecord
from rick.form import RequestRecord, field
class UserRequest(RequestRecord):
fields = {
'username': field(validators='required|alphanum|minlen:3'),
'email': field(validators='required|email'),
'age': field(validators='numeric|between:18,120'),
}
# Validate request data
request = UserRequest()
data = {'username': 'alice', 'email': 'alice@example.com', 'age': '25'}
if request.is_valid(data):
print("Valid request!")
user_data = request.get_data()
Form Features
Adding Fields
form = Form()
# Basic field
form.field("text", "name", "Full Name", validators="required")
# Field with multiple validators
form.field("text", "age", "Age", validators="required|numeric|between:18,120")
# Optional field (no 'required' validator)
form.field("text", "phone", "Phone", validators="numeric")
Using Fieldsets
form = Form()
# Personal information fieldset
personal = form.fieldset("personal", "Personal Information")
personal.field("text", "first_name", "First Name", validators="required")
personal.field("text", "last_name", "Last Name", validators="required")
# Contact information fieldset
contact = form.fieldset("contact", "Contact Information")
contact.field("email", "email", "Email", validators="required|email")
contact.field("text", "phone", "Phone", validators="numeric")
Adding Controls
form = Form()
form.field("text", "username", "Username", validators="required")
form.field("password", "password", "Password", validators="required")
# Add form controls (buttons)
form.control("submit", "login", "Login")
form.control("button", "cancel", "Cancel")
Form Configuration
form = Form()
form.set_action("/api/users")
form.set_method(Form.METHOD_POST)
# Available methods:
# - Form.METHOD_POST
# - Form.METHOD_PUT
# - Form.METHOD_PATCH
# - Form.METHOD_SEARCH
Custom Validation
Custom validator methods are executed automatically after standard field validation passes. The method name must follow
the pattern validator_<field_id>().
Note: Custom validators only run if all field validators pass first. This ensures you don't need to repeat basic validation logic in your custom methods.
Basic Custom Validation
from rick.form import Form
from rick.mixin import Translator
class RegistrationForm(Form):
def __init__(self):
super().__init__()
self.field('text', 'username', 'Username', validators="required|alphanum")
self.field('password', 'password', 'Password', validators="required|minlen:8")
self.field('password', 'confirm', 'Confirm Password', validators="required")
def validator_confirm(self, data, t: Translator):
"""Validate password confirmation"""
if data['password'] != data['confirm']:
self.add_error('confirm', 'Passwords do not match')
return False
return True
def validator_username(self, data, t: Translator):
"""Check if username is available"""
# Simulate database check
taken_usernames = ['admin', 'root', 'system']
if data['username'].lower() in taken_usernames:
self.add_error('username', 'Username not available')
return False
return True
Multi-Field Validation
from rick.form import Form
from rick.mixin import Translator
class DateRangeForm(Form):
def __init__(self):
super().__init__()
self.field('date', 'start_date', 'Start Date', validators="required")
self.field('date', 'end_date', 'End Date', validators="required")
def validator_end_date(self, data, t: Translator):
"""Ensure end date is after start date"""
start = data.get('start_date', '')
end = data.get('end_date', '')
if start and end and end < start:
self.add_error('end_date', 'End date must be after start date')
return False
return True
Working with Form Data
Getting Field Values
form = Form()
form.field("text", "name", "Name", validators="required")
form.field("email", "email", "Email", validators="required|email")
data = {"name": "Alice", "email": "alice@example.com"}
if form.is_valid(data):
# Get individual field
name = form.get('name')
# Get all data as dict
all_data = form.get_data()
Setting Field Values
form = Form()
form.field("text", "name", "Name")
form.field("email", "email", "Email")
# Set values programmatically
form.set("name", "Bob")
form.set("email", "bob@example.com")
print(form.get('name')) # Output: Bob
Validation Errors
After validation fails, retrieve errors using get_errors():
form = Form()
form.field("text", "username", "Username", validators="required|minlen:3")
form.field("email", "email", "Email", validators="required|email")
data = {"username": "ab", "email": "invalid"}
if not form.is_valid(data):
errors = form.get_errors()
# {
# 'username': {'minlen': 'minimum allowed length is 3'},
# 'email': {'email': 'invalid email address'}
# }
See Error Format Documentation for complete error structure details.
Available Validators
Rick includes many built-in validators. Common validators include:
String Validators:
required- Field must be present and not emptyalpha- Alphabetic characters onlyalphanum- Alphanumeric characters onlyminlen:N- Minimum lengthmaxlen:N- Maximum length
Numeric Validators:
numeric- Must be numericint- Must be integerdecimal- Must be decimalbetween:min,max- Value range
Network Validators:
email- Valid email addressipv4/ipv6- IP addressesfqdn- Fully qualified domain name
See the Validators Documentation for the complete list.
Examples
Rick includes comprehensive form examples in examples/form/:
- simple_form.py - Basic form usage
- custom_validation.py - Custom validators and business logic
- fieldset_example.py - Organizing forms with fieldsets
- request_record_example.py - API validation and nested records
Run examples:
See Also
- Form Class Reference - Complete Form API
- Field Class Reference - Field options and parameters
- RequestRecord Documentation - API request validation
- Error Format - Error message structure
- Validators - Available validation rules