Forms and User Input

Advanced Form Techniques

Django's form system supports sophisticated patterns for complex user interfaces, including multi-step forms, dynamic field generation, AJAX integration, and custom validation workflows. This chapter covers advanced techniques for building professional-grade form experiences.

Advanced Form Techniques

Django's form system supports sophisticated patterns for complex user interfaces, including multi-step forms, dynamic field generation, AJAX integration, and custom validation workflows. This chapter covers advanced techniques for building professional-grade form experiences.

Multi-Step Forms

Wizard-Style Form Implementation

# forms.py - Multi-step form classes
from django import forms
from django.core.exceptions import ValidationError

class PersonalInfoForm(forms.Form):
    """Step 1: Personal Information"""
    
    first_name = forms.CharField(
        max_length=50,
        widget=forms.TextInput(attrs={'class': 'form-control'})
    )
    last_name = forms.CharField(
        max_length=50,
        widget=forms.TextInput(attrs={'class': 'form-control'})
    )
    email = forms.EmailField(
        widget=forms.EmailInput(attrs={'class': 'form-control'})
    )
    phone = forms.CharField(
        max_length=15,
        widget=forms.TextInput(attrs={'class': 'form-control'})
    )
    date_of_birth = forms.DateField(
        widget=forms.DateInput(attrs={'class': 'form-control', 'type': 'date'})
    )

class AddressInfoForm(forms.Form):
    """Step 2: Address Information"""
    
    street_address = forms.CharField(
        max_length=200,
        widget=forms.TextInput(attrs={'class': 'form-control'})
    )
    city = forms.CharField(
        max_length=100,
        widget=forms.TextInput(attrs={'class': 'form-control'})
    )
    state = forms.CharField(
        max_length=50,
        widget=forms.TextInput(attrs={'class': 'form-control'})
    )
    zip_code = forms.CharField(
        max_length=10,
        widget=forms.TextInput(attrs={'class': 'form-control'})
    )
    country = forms.ChoiceField(
        choices=[
            ('US', 'United States'),
            ('CA', 'Canada'),
            ('UK', 'United Kingdom'),
        ],
        widget=forms.Select(attrs={'class': 'form-control'})
    )

class PreferencesForm(forms.Form):
    """Step 3: Preferences"""
    
    newsletter = forms.BooleanField(
        required=False,
        widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
    )
    notifications = forms.MultipleChoiceField(
        choices=[
            ('email', 'Email Notifications'),
            ('sms', 'SMS Notifications'),
            ('push', 'Push Notifications'),
        ],
        widget=forms.CheckboxSelectMultiple,
        required=False
    )
    language = forms.ChoiceField(
        choices=[
            ('en', 'English'),
            ('es', 'Spanish'),
            ('fr', 'French'),
        ],
        widget=forms.Select(attrs={'class': 'form-control'})
    )

class MultiStepFormWizard:
    """Multi-step form wizard handler"""
    
    FORMS = [
        ('personal', PersonalInfoForm),
        ('address', AddressInfoForm),
        ('preferences', PreferencesForm),
    ]
    
    def __init__(self, request):
        self.request = request
        self.current_step = self.get_current_step()
        self.form_data = self.get_form_data()
    
    def get_current_step(self):
        """Get current step from session"""
        return self.request.session.get('wizard_step', 0)
    
    def set_current_step(self, step):
        """Set current step in session"""
        self.request.session['wizard_step'] = step
        self.current_step = step
    
    def get_form_data(self):
        """Get accumulated form data from session"""
        return self.request.session.get('wizard_data', {})
    
    def set_form_data(self, step_name, data):
        """Store step data in session"""
        if 'wizard_data' not in self.request.session:
            self.request.session['wizard_data'] = {}
        
        self.request.session['wizard_data'][step_name] = data
        self.request.session.modified = True
    
    def get_current_form_class(self):
        """Get form class for current step"""
        if self.current_step < len(self.FORMS):
            return self.FORMS[self.current_step][1]
        return None
    
    def get_current_step_name(self):
        """Get name of current step"""
        if self.current_step < len(self.FORMS):
            return self.FORMS[self.current_step][0]
        return None
    
    def is_last_step(self):
        """Check if current step is the last one"""
        return self.current_step >= len(self.FORMS) - 1
    
    def get_progress_percentage(self):
        """Calculate progress percentage"""
        return int((self.current_step + 1) / len(self.FORMS) * 100)
    
    def process_step(self, form):
        """Process current step and move to next"""
        step_name = self.get_current_step_name()
        self.set_form_data(step_name, form.cleaned_data)
        
        if not self.is_last_step():
            self.set_current_step(self.current_step + 1)
            return False  # Not finished
        else:
            return True  # Wizard complete
    
    def go_back(self):
        """Go back to previous step"""
        if self.current_step > 0:
            self.set_current_step(self.current_step - 1)
    
    def get_all_data(self):
        """Get all collected data"""
        return self.form_data
    
    def clear_data(self):
        """Clear wizard data from session"""
        if 'wizard_data' in self.request.session:
            del self.request.session['wizard_data']
        if 'wizard_step' in self.request.session:
            del self.request.session['wizard_step']

# views.py - Multi-step form view
from django.shortcuts import render, redirect
from django.contrib import messages
from .forms import MultiStepFormWizard

def multi_step_form_view(request):
    wizard = MultiStepFormWizard(request)
    
    # Handle back button
    if request.POST.get('back'):
        wizard.go_back()
        return redirect('multi_step_form')
    
    # Get current form
    form_class = wizard.get_current_form_class()
    if not form_class:
        return redirect('form_complete')
    
    if request.method == 'POST':
        form = form_class(request.POST)
        if form.is_valid():
            # Process step
            is_complete = wizard.process_step(form)
            
            if is_complete:
                # Process final data
                all_data = wizard.get_all_data()
                process_registration_data(all_data)
                wizard.clear_data()
                
                messages.success(request, 'Registration completed successfully!')
                return redirect('registration_complete')
            else:
                return redirect('multi_step_form')
    else:
        # Pre-populate form with existing data
        step_name = wizard.get_current_step_name()
        initial_data = wizard.form_data.get(step_name, {})
        form = form_class(initial=initial_data)
    
    context = {
        'form': form,
        'current_step': wizard.current_step + 1,
        'total_steps': len(wizard.FORMS),
        'step_name': wizard.get_current_step_name(),
        'progress_percentage': wizard.get_progress_percentage(),
        'is_last_step': wizard.is_last_step(),
        'can_go_back': wizard.current_step > 0,
    }
    
    return render(request, 'forms/multi_step.html', context)

def process_registration_data(data):
    """Process complete registration data"""
    # Combine all step data and create user account
    personal = data.get('personal', {})
    address = data.get('address', {})
    preferences = data.get('preferences', {})
    
    # Create user and profile
    from django.contrib.auth.models import User
    user = User.objects.create_user(
        username=personal['email'],
        email=personal['email'],
        first_name=personal['first_name'],
        last_name=personal['last_name']
    )
    
    # Create profile with additional data
    from .models import UserProfile
    UserProfile.objects.create(
        user=user,
        phone=personal['phone'],
        date_of_birth=personal['date_of_birth'],
        street_address=address['street_address'],
        city=address['city'],
        state=address['state'],
        zip_code=address['zip_code'],
        country=address['country'],
        newsletter=preferences.get('newsletter', False),
        language=preferences['language']
    )

Session-Based Form State Management

# utils.py - Form state management utilities
import json
from django.core.serializers.json import DjangoJSONEncoder

class FormStateManager:
    """Manage form state across requests"""
    
    def __init__(self, request, form_key):
        self.request = request
        self.form_key = form_key
        self.session_key = f'form_state_{form_key}'
    
    def save_state(self, form_data, step=None, metadata=None):
        """Save form state to session"""
        state = {
            'data': form_data,
            'step': step,
            'metadata': metadata or {},
            'timestamp': timezone.now().isoformat()
        }
        
        self.request.session[self.session_key] = json.dumps(
            state, cls=DjangoJSONEncoder
        )
        self.request.session.modified = True
    
    def load_state(self):
        """Load form state from session"""
        state_json = self.request.session.get(self.session_key)
        if state_json:
            try:
                return json.loads(state_json)
            except (json.JSONDecodeError, ValueError):
                pass
        return None
    
    def clear_state(self):
        """Clear form state from session"""
        if self.session_key in self.request.session:
            del self.request.session[self.session_key]
    
    def get_data(self):
        """Get form data from state"""
        state = self.load_state()
        return state['data'] if state else {}
    
    def get_step(self):
        """Get current step from state"""
        state = self.load_state()
        return state['step'] if state else 0
    
    def get_metadata(self):
        """Get metadata from state"""
        state = self.load_state()
        return state['metadata'] if state else {}

# forms.py - Stateful form mixin
from django import forms
from django.utils import timezone

class StatefulFormMixin:
    """Mixin for forms that maintain state"""
    
    def __init__(self, *args, **kwargs):
        self.state_manager = kwargs.pop('state_manager', None)
        super().__init__(*args, **kwargs)
        
        if self.state_manager:
            self.load_from_state()
    
    def load_from_state(self):
        """Load form data from state manager"""
        if self.state_manager and not self.data:
            saved_data = self.state_manager.get_data()
            if saved_data:
                # Update initial values
                for field_name, value in saved_data.items():
                    if field_name in self.fields:
                        self.fields[field_name].initial = value
    
    def save_to_state(self, step=None, metadata=None):
        """Save form data to state manager"""
        if self.state_manager and self.is_valid():
            self.state_manager.save_state(
                self.cleaned_data,
                step=step,
                metadata=metadata
            )
    
    def clear_state(self):
        """Clear saved state"""
        if self.state_manager:
            self.state_manager.clear_state()

class StatefulContactForm(StatefulFormMixin, forms.Form):
    """Contact form with state management"""
    
    name = forms.CharField(max_length=100)
    email = forms.EmailField()
    subject = forms.CharField(max_length=200)
    message = forms.CharField(widget=forms.Textarea)
    
    def save_draft(self):
        """Save form as draft"""
        if self.state_manager:
            metadata = {
                'is_draft': True,
                'saved_at': timezone.now().isoformat()
            }
            self.save_to_state(metadata=metadata)

Dynamic Form Generation

Runtime Field Creation

# forms.py - Dynamic form generation
from django import forms
from django.core.exceptions import ValidationError

class DynamicFormBuilder:
    """Build forms dynamically based on configuration"""
    
    FIELD_TYPES = {
        'text': forms.CharField,
        'email': forms.EmailField,
        'number': forms.IntegerField,
        'decimal': forms.DecimalField,
        'date': forms.DateField,
        'datetime': forms.DateTimeField,
        'boolean': forms.BooleanField,
        'choice': forms.ChoiceField,
        'multiple_choice': forms.MultipleChoiceField,
        'file': forms.FileField,
        'image': forms.ImageField,
        'textarea': forms.CharField,
    }
    
    WIDGET_TYPES = {
        'text': forms.TextInput,
        'textarea': forms.Textarea,
        'email': forms.EmailInput,
        'password': forms.PasswordInput,
        'number': forms.NumberInput,
        'date': forms.DateInput,
        'datetime': forms.DateTimeInput,
        'checkbox': forms.CheckboxInput,
        'radio': forms.RadioSelect,
        'select': forms.Select,
        'select_multiple': forms.SelectMultiple,
        'checkbox_multiple': forms.CheckboxSelectMultiple,
        'file': forms.FileInput,
        'hidden': forms.HiddenInput,
    }
    
    @classmethod
    def create_form(cls, field_configs, form_name='DynamicForm'):
        """Create form class from field configurations"""
        
        form_fields = {}
        
        for config in field_configs:
            field = cls.create_field(config)
            if field:
                form_fields[config['name']] = field
        
        # Create form class dynamically
        form_class = type(form_name, (forms.Form,), form_fields)
        
        # Add custom validation if specified
        if any('validation' in config for config in field_configs):
            cls.add_custom_validation(form_class, field_configs)
        
        return form_class
    
    @classmethod
    def create_field(cls, config):
        """Create individual field from configuration"""
        field_type = config.get('type', 'text')
        field_class = cls.FIELD_TYPES.get(field_type)
        
        if not field_class:
            return None
        
        # Basic field parameters
        field_kwargs = {
            'label': config.get('label', config['name'].title()),
            'required': config.get('required', True),
            'help_text': config.get('help_text', ''),
            'initial': config.get('initial'),
        }
        
        # Type-specific parameters
        if field_type in ['text', 'textarea']:
            field_kwargs.update({
                'max_length': config.get('max_length', 255),
                'min_length': config.get('min_length'),
            })
            
            if field_type == 'textarea':
                field_kwargs['widget'] = forms.Textarea(attrs={
                    'rows': config.get('rows', 4)
                })
        
        elif field_type in ['number', 'decimal']:
            field_kwargs.update({
                'min_value': config.get('min_value'),
                'max_value': config.get('max_value'),
            })
            
            if field_type == 'decimal':
                field_kwargs.update({
                    'max_digits': config.get('max_digits', 10),
                    'decimal_places': config.get('decimal_places', 2),
                })
        
        elif field_type in ['choice', 'multiple_choice']:
            choices = config.get('choices', [])
            field_kwargs['choices'] = choices
            
            # Custom widget for choice fields
            widget_type = config.get('widget', 'select')
            if widget_type in cls.WIDGET_TYPES:
                field_kwargs['widget'] = cls.WIDGET_TYPES[widget_type]
        
        # Custom widget attributes
        widget_attrs = config.get('widget_attrs', {})
        if widget_attrs:
            if 'widget' not in field_kwargs:
                widget_type = config.get('widget', field_type)
                widget_class = cls.WIDGET_TYPES.get(widget_type, forms.TextInput)
                field_kwargs['widget'] = widget_class(attrs=widget_attrs)
            else:
                field_kwargs['widget'].attrs.update(widget_attrs)
        
        # Custom validators
        validators = config.get('validators', [])
        if validators:
            field_kwargs['validators'] = cls.create_validators(validators)
        
        return field_class(**field_kwargs)
    
    @classmethod
    def create_validators(cls, validator_configs):
        """Create validators from configuration"""
        validators = []
        
        for validator_config in validator_configs:
            validator_type = validator_config.get('type')
            
            if validator_type == 'regex':
                from django.core.validators import RegexValidator
                validators.append(RegexValidator(
                    regex=validator_config['pattern'],
                    message=validator_config.get('message', 'Invalid format')
                ))
            
            elif validator_type == 'min_length':
                from django.core.validators import MinLengthValidator
                validators.append(MinLengthValidator(
                    validator_config['value'],
                    message=validator_config.get('message')
                ))
            
            elif validator_type == 'max_length':
                from django.core.validators import MaxLengthValidator
                validators.append(MaxLengthValidator(
                    validator_config['value'],
                    message=validator_config.get('message')
                ))
        
        return validators
    
    @classmethod
    def add_custom_validation(cls, form_class, field_configs):
        """Add custom validation methods to form class"""
        
        def clean(self):
            cleaned_data = super(form_class, self).clean()
            
            # Cross-field validation
            for config in field_configs:
                validation = config.get('validation', {})
                
                if 'depends_on' in validation:
                    cls.validate_dependency(
                        cleaned_data, 
                        config['name'], 
                        validation['depends_on'],
                        validation.get('condition')
                    )
            
            return cleaned_data
        
        form_class.clean = clean
    
    @classmethod
    def validate_dependency(cls, data, field_name, depends_on, condition):
        """Validate field dependencies"""
        field_value = data.get(field_name)
        depends_value = data.get(depends_on)
        
        if condition == 'required_if_true' and depends_value and not field_value:
            raise ValidationError({
                field_name: f'This field is required when {depends_on} is selected.'
            })
        
        elif condition == 'required_if_false' and not depends_value and not field_value:
            raise ValidationError({
                field_name: f'This field is required when {depends_on} is not selected.'
            })

# Usage example
field_configurations = [
    {
        'name': 'first_name',
        'type': 'text',
        'label': 'First Name',
        'required': True,
        'max_length': 50,
        'widget_attrs': {'class': 'form-control'}
    },
    {
        'name': 'email',
        'type': 'email',
        'label': 'Email Address',
        'required': True,
        'widget_attrs': {'class': 'form-control'}
    },
    {
        'name': 'age',
        'type': 'number',
        'label': 'Age',
        'required': True,
        'min_value': 18,
        'max_value': 100,
        'widget_attrs': {'class': 'form-control'}
    },
    {
        'name': 'country',
        'type': 'choice',
        'label': 'Country',
        'required': True,
        'choices': [
            ('us', 'United States'),
            ('ca', 'Canada'),
            ('uk', 'United Kingdom')
        ],
        'widget_attrs': {'class': 'form-control'}
    },
    {
        'name': 'newsletter',
        'type': 'boolean',
        'label': 'Subscribe to Newsletter',
        'required': False,
        'widget_attrs': {'class': 'form-check-input'}
    }
]

# Create dynamic form
DynamicUserForm = DynamicFormBuilder.create_form(
    field_configurations, 
    'UserRegistrationForm'
)

# views.py - Using dynamic forms
def dynamic_form_view(request):
    form_class = DynamicFormBuilder.create_form(field_configurations)
    
    if request.method == 'POST':
        form = form_class(request.POST)
        if form.is_valid():
            # Process form data
            process_dynamic_form_data(form.cleaned_data)
            return redirect('success')
    else:
        form = form_class()
    
    return render(request, 'forms/dynamic.html', {'form': form})

AJAX Form Integration

Asynchronous Form Submission

# views.py - AJAX form handling
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from django.views.generic import View
import json

class AjaxFormView(View):
    """Base view for AJAX form handling"""
    
    form_class = None
    template_name = None
    
    def get(self, request, *args, **kwargs):
        form = self.get_form()
        return self.render_form(request, form)
    
    def post(self, request, *args, **kwargs):
        if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
            return self.handle_ajax_request(request)
        else:
            return self.handle_regular_request(request)
    
    def handle_ajax_request(self, request):
        """Handle AJAX form submission"""
        form = self.get_form(request.POST, request.FILES)
        
        if form.is_valid():
            result = self.form_valid(form)
            return JsonResponse({
                'success': True,
                'message': 'Form submitted successfully!',
                'data': result
            })
        else:
            return JsonResponse({
                'success': False,
                'errors': form.errors,
                'non_field_errors': form.non_field_errors()
            }, status=400)
    
    def handle_regular_request(self, request):
        """Handle regular form submission"""
        form = self.get_form(request.POST, request.FILES)
        
        if form.is_valid():
            self.form_valid(form)
            return redirect(self.get_success_url())
        
        return self.render_form(request, form)
    
    def get_form(self, data=None, files=None):
        """Get form instance"""
        return self.form_class(data=data, files=files)
    
    def form_valid(self, form):
        """Process valid form"""
        return form.cleaned_data
    
    def render_form(self, request, form):
        """Render form template"""
        return render(request, self.template_name, {'form': form})
    
    def get_success_url(self):
        """Get success redirect URL"""
        return '/'

class ContactAjaxView(AjaxFormView):
    """AJAX contact form view"""
    
    form_class = ContactForm
    template_name = 'forms/contact_ajax.html'
    
    def form_valid(self, form):
        """Process contact form"""
        # Send email
        send_contact_email(
            name=form.cleaned_data['name'],
            email=form.cleaned_data['email'],
            message=form.cleaned_data['message']
        )
        
        return {
            'contact_id': generate_contact_id(),
            'timestamp': timezone.now().isoformat()
        }

# JavaScript for AJAX form handling
"""
// static/js/ajax-forms.js
class AjaxForm {
    constructor(formSelector, options = {}) {
        this.form = document.querySelector(formSelector);
        this.options = {
            showLoading: true,
            resetOnSuccess: true,
            showMessages: true,
            ...options
        };
        
        this.init();
    }
    
    init() {
        if (!this.form) return;
        
        this.form.addEventListener('submit', (e) => {
            e.preventDefault();
            this.submitForm();
        });
    }
    
    async submitForm() {
        const formData = new FormData(this.form);
        const submitButton = this.form.querySelector('[type="submit"]');
        
        // Show loading state
        if (this.options.showLoading) {
            this.setLoadingState(submitButton, true);
        }
        
        // Clear previous errors
        this.clearErrors();
        
        try {
            const response = await fetch(this.form.action || window.location.href, {
                method: 'POST',
                body: formData,
                headers: {
                    'X-Requested-With': 'XMLHttpRequest',
                    'X-CSRFToken': this.getCSRFToken()
                }
            });
            
            const data = await response.json();
            
            if (data.success) {
                this.handleSuccess(data);
            } else {
                this.handleErrors(data);
            }
            
        } catch (error) {
            console.error('Form submission error:', error);
            this.showMessage('An error occurred. Please try again.', 'error');
        } finally {
            if (this.options.showLoading) {
                this.setLoadingState(submitButton, false);
            }
        }
    }
    
    handleSuccess(data) {
        if (this.options.showMessages) {
            this.showMessage(data.message || 'Success!', 'success');
        }
        
        if (this.options.resetOnSuccess) {
            this.form.reset();
        }
        
        // Trigger custom success event
        this.form.dispatchEvent(new CustomEvent('ajaxSuccess', {
            detail: data
        }));
    }
    
    handleErrors(data) {
        // Show field errors
        if (data.errors) {
            for (const [fieldName, errors] of Object.entries(data.errors)) {
                this.showFieldError(fieldName, errors);
            }
        }
        
        // Show non-field errors
        if (data.non_field_errors) {
            for (const error of data.non_field_errors) {
                this.showMessage(error, 'error');
            }
        }
        
        // Trigger custom error event
        this.form.dispatchEvent(new CustomEvent('ajaxError', {
            detail: data
        }));
    }
    
    showFieldError(fieldName, errors) {
        const field = this.form.querySelector(`[name="${fieldName}"]`);
        if (!field) return;
        
        const errorContainer = this.getOrCreateErrorContainer(field);
        errorContainer.textContent = Array.isArray(errors) ? errors[0] : errors;
        errorContainer.style.display = 'block';
        
        field.classList.add('is-invalid');
    }
    
    clearErrors() {
        // Clear field errors
        this.form.querySelectorAll('.field-error').forEach(el => {
            el.style.display = 'none';
            el.textContent = '';
        });
        
        this.form.querySelectorAll('.is-invalid').forEach(el => {
            el.classList.remove('is-invalid');
        });
        
        // Clear message container
        const messageContainer = this.form.querySelector('.form-messages');
        if (messageContainer) {
            messageContainer.innerHTML = '';
        }
    }
    
    getOrCreateErrorContainer(field) {
        let errorContainer = field.parentNode.querySelector('.field-error');
        
        if (!errorContainer) {
            errorContainer = document.createElement('div');
            errorContainer.className = 'field-error text-danger small';
            field.parentNode.appendChild(errorContainer);
        }
        
        return errorContainer;
    }
    
    showMessage(message, type = 'info') {
        if (!this.options.showMessages) return;
        
        let messageContainer = this.form.querySelector('.form-messages');
        
        if (!messageContainer) {
            messageContainer = document.createElement('div');
            messageContainer.className = 'form-messages';
            this.form.insertBefore(messageContainer, this.form.firstChild);
        }
        
        const alertClass = {
            'success': 'alert-success',
            'error': 'alert-danger',
            'warning': 'alert-warning',
            'info': 'alert-info'
        }[type] || 'alert-info';
        
        messageContainer.innerHTML = `
            <div class="alert ${alertClass} alert-dismissible fade show" role="alert">
                ${message}
                <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
            </div>
        `;
    }
    
    setLoadingState(button, loading) {
        if (loading) {
            button.disabled = true;
            button.dataset.originalText = button.textContent;
            button.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Loading...';
        } else {
            button.disabled = false;
            button.textContent = button.dataset.originalText || 'Submit';
        }
    }
    
    getCSRFToken() {
        const csrfInput = this.form.querySelector('[name="csrfmiddlewaretoken"]');
        return csrfInput ? csrfInput.value : '';
    }
}

// Initialize AJAX forms
document.addEventListener('DOMContentLoaded', function() {
    // Initialize all forms with .ajax-form class
    document.querySelectorAll('.ajax-form').forEach(form => {
        new AjaxForm(`#${form.id}`);
    });
});
"""

Form Performance Optimization

Efficient Form Rendering and Processing

# forms.py - Performance optimized forms
from django import forms
from django.core.cache import cache
from django.db import transaction

class OptimizedForm(forms.Form):
    """Base form with performance optimizations"""
    
    def __init__(self, *args, **kwargs):
        # Cache expensive operations
        self.cached_choices = kwargs.pop('cached_choices', True)
        super().__init__(*args, **kwargs)
        
        if self.cached_choices:
            self.load_cached_choices()
    
    def load_cached_choices(self):
        """Load choices from cache"""
        for field_name, field in self.fields.items():
            if hasattr(field, 'choices') and hasattr(field, 'queryset'):
                cache_key = f'form_choices_{field_name}_{self.__class__.__name__}'
                cached_choices = cache.get(cache_key)
                
                if cached_choices is None:
                    # Generate choices and cache them
                    choices = list(field.choices)
                    cache.set(cache_key, choices, 300)  # 5 minutes
                    field.choices = choices
                else:
                    field.choices = cached_choices
    
    @transaction.atomic
    def save(self):
        """Atomic save operation"""
        return self.process_form_data()
    
    def process_form_data(self):
        """Override in subclasses"""
        pass

class BulkProcessingForm(forms.Form):
    """Form for bulk operations with progress tracking"""
    
    items = forms.ModelMultipleChoiceField(
        queryset=None,
        widget=forms.CheckboxSelectMultiple
    )
    
    action = forms.ChoiceField(choices=[])
    
    def __init__(self, *args, **kwargs):
        self.queryset = kwargs.pop('queryset', None)
        self.actions = kwargs.pop('actions', [])
        super().__init__(*args, **kwargs)
        
        if self.queryset is not None:
            self.fields['items'].queryset = self.queryset
        
        if self.actions:
            self.fields['action'].choices = self.actions
    
    def process_bulk_action(self, progress_callback=None):
        """Process bulk action with progress tracking"""
        items = self.cleaned_data['items']
        action = self.cleaned_data['action']
        
        total_items = items.count()
        processed = 0
        
        for item in items.iterator():  # Use iterator for memory efficiency
            self.process_single_item(item, action)
            processed += 1
            
            if progress_callback:
                progress_callback(processed, total_items)
        
        return processed
    
    def process_single_item(self, item, action):
        """Process individual item"""
        # Override in subclasses
        pass

# Lazy loading for large datasets
class LazyChoiceField(forms.ChoiceField):
    """Choice field with lazy loading"""
    
    def __init__(self, choice_loader=None, *args, **kwargs):
        self.choice_loader = choice_loader
        super().__init__(*args, **kwargs)
    
    @property
    def choices(self):
        if self.choice_loader and not hasattr(self, '_choices'):
            self._choices = self.choice_loader()
        return self._choices
    
    @choices.setter
    def choices(self, value):
        self._choices = value

def load_country_choices():
    """Lazy loader for country choices"""
    # This could load from database, API, or file
    return [
        ('us', 'United States'),
        ('ca', 'Canada'),
        ('uk', 'United Kingdom'),
        # ... more countries
    ]

class LazyForm(forms.Form):
    """Form with lazy-loaded choices"""
    
    country = LazyChoiceField(choice_loader=load_country_choices)

Advanced form techniques enable sophisticated user experiences while maintaining performance and maintainability. By implementing multi-step wizards, dynamic field generation, AJAX integration, and performance optimizations, you can build professional-grade forms that handle complex requirements efficiently and provide excellent user experiences across all devices and interaction patterns.