Django provides a comprehensive set of form fields and widgets that handle different data types and user interface elements. Understanding these built-in components enables you to create sophisticated forms without custom implementations.
# forms.py - Text field variations
from django import forms
class TextFieldExamplesForm(forms.Form):
"""Comprehensive text field examples"""
# Basic CharField
name = forms.CharField(
max_length=100,
min_length=2,
label='Full Name',
help_text='Enter your complete name',
initial='',
required=True
)
# CharField with custom widget
description = forms.CharField(
widget=forms.Textarea(attrs={
'rows': 4,
'cols': 50,
'placeholder': 'Enter description...'
}),
max_length=500,
help_text='Maximum 500 characters'
)
# Slug field for URL-friendly strings
slug = forms.SlugField(
max_length=50,
help_text='URL-friendly version (letters, numbers, hyphens, underscores only)'
)
# RegexField for pattern matching
product_code = forms.RegexField(
regex=r'^[A-Z]{2}\d{4}$',
max_length=6,
error_message='Product code must be 2 uppercase letters followed by 4 digits (e.g., AB1234)',
help_text='Format: AB1234'
)
# CharField with choices (acts like a select)
STATUS_CHOICES = [
('draft', 'Draft'),
('published', 'Published'),
('archived', 'Archived'),
]
status = forms.CharField(
max_length=20,
choices=STATUS_CHOICES,
initial='draft'
)
# Password field
password = forms.CharField(
widget=forms.PasswordInput(attrs={
'placeholder': 'Enter password',
'autocomplete': 'new-password'
}),
min_length=8,
help_text='Minimum 8 characters'
)
# Hidden field
csrf_token = forms.CharField(
widget=forms.HiddenInput(),
required=False
)
# forms.py - Numeric field types
from django import forms
from decimal import Decimal
class NumericFieldsForm(forms.Form):
"""Examples of numeric form fields"""
# Integer field
age = forms.IntegerField(
min_value=0,
max_value=150,
help_text='Enter your age in years',
widget=forms.NumberInput(attrs={
'placeholder': 'Age',
'step': '1'
})
)
# Float field
rating = forms.FloatField(
min_value=0.0,
max_value=5.0,
help_text='Rating from 0.0 to 5.0',
widget=forms.NumberInput(attrs={
'step': '0.1',
'placeholder': '0.0'
})
)
# Decimal field for precise calculations
price = forms.DecimalField(
max_digits=10,
decimal_places=2,
min_value=Decimal('0.01'),
max_value=Decimal('999999.99'),
help_text='Price in USD',
widget=forms.NumberInput(attrs={
'step': '0.01',
'placeholder': '0.00'
})
)
# Integer field with range widget
priority = forms.IntegerField(
min_value=1,
max_value=10,
initial=5,
widget=forms.NumberInput(attrs={
'type': 'range',
'step': '1'
}),
help_text='Priority level (1-10)'
)
# Percentage field (using DecimalField)
completion = forms.DecimalField(
max_digits=5,
decimal_places=2,
min_value=Decimal('0.00'),
max_value=Decimal('100.00'),
widget=forms.NumberInput(attrs={
'step': '0.01',
'placeholder': '0.00',
'suffix': '%'
}),
help_text='Completion percentage'
)
# forms.py - Date and time fields
from django import forms
from datetime import date, datetime, time
class DateTimeFieldsForm(forms.Form):
"""Date and time field examples"""
# Date field
birth_date = forms.DateField(
help_text='Format: YYYY-MM-DD',
widget=forms.DateInput(attrs={
'type': 'date',
'max': date.today().isoformat()
})
)
# Time field
appointment_time = forms.TimeField(
help_text='Format: HH:MM',
widget=forms.TimeInput(attrs={
'type': 'time',
'step': '900' # 15-minute intervals
})
)
# DateTime field
event_datetime = forms.DateTimeField(
help_text='Event date and time',
widget=forms.DateTimeInput(attrs={
'type': 'datetime-local'
})
)
# Date field with custom widget
project_deadline = forms.DateField(
widget=forms.DateInput(attrs={
'type': 'date',
'min': date.today().isoformat(),
'class': 'form-control'
}),
help_text='Project completion deadline'
)
# Duration field (using CharField with custom validation)
duration = forms.DurationField(
help_text='Duration in format: DD HH:MM:SS or HH:MM:SS'
)
# Custom date range validation
start_date = forms.DateField(
widget=forms.DateInput(attrs={'type': 'date'})
)
end_date = forms.DateField(
widget=forms.DateInput(attrs={'type': 'date'})
)
def clean(self):
cleaned_data = super().clean()
start_date = cleaned_data.get('start_date')
end_date = cleaned_data.get('end_date')
if start_date and end_date:
if end_date < start_date:
raise forms.ValidationError('End date must be after start date.')
return cleaned_data
# forms.py - Choice field variations
from django import forms
class ChoiceFieldsForm(forms.Form):
"""Examples of choice-based fields"""
# Basic choice field (dropdown)
CATEGORY_CHOICES = [
('', 'Select Category'),
('tech', 'Technology'),
('health', 'Health & Fitness'),
('education', 'Education'),
('entertainment', 'Entertainment'),
]
category = forms.ChoiceField(
choices=CATEGORY_CHOICES,
help_text='Select the most appropriate category'
)
# Choice field with radio buttons
PRIORITY_CHOICES = [
('low', 'Low Priority'),
('medium', 'Medium Priority'),
('high', 'High Priority'),
('urgent', 'Urgent'),
]
priority = forms.ChoiceField(
choices=PRIORITY_CHOICES,
widget=forms.RadioSelect,
initial='medium',
help_text='Select priority level'
)
# Multiple choice field with checkboxes
SKILL_CHOICES = [
('python', 'Python'),
('javascript', 'JavaScript'),
('java', 'Java'),
('csharp', 'C#'),
('php', 'PHP'),
('ruby', 'Ruby'),
]
skills = forms.MultipleChoiceField(
choices=SKILL_CHOICES,
widget=forms.CheckboxSelectMultiple,
required=False,
help_text='Select all applicable skills'
)
# Multiple choice with select multiple
languages = forms.MultipleChoiceField(
choices=[
('en', 'English'),
('es', 'Spanish'),
('fr', 'French'),
('de', 'German'),
('it', 'Italian'),
('pt', 'Portuguese'),
],
widget=forms.SelectMultiple(attrs={
'size': '4',
'class': 'form-control'
}),
help_text='Hold Ctrl/Cmd to select multiple languages'
)
# Typed choice field (converts values to specific type)
difficulty_level = forms.TypedChoiceField(
choices=[
(1, 'Beginner'),
(2, 'Intermediate'),
(3, 'Advanced'),
(4, 'Expert'),
],
coerce=int, # Convert to integer
widget=forms.RadioSelect,
help_text='Select your skill level'
)
# Boolean field variations
is_active = forms.BooleanField(
required=False,
help_text='Check to activate'
)
terms_accepted = forms.BooleanField(
help_text='You must accept the terms and conditions',
error_messages={
'required': 'You must accept the terms and conditions to proceed.'
}
)
# Null boolean field (True/False/None)
newsletter_subscription = forms.NullBooleanField(
widget=forms.Select(choices=[
(None, 'Not specified'),
(True, 'Yes, subscribe me'),
(False, 'No, do not subscribe'),
]),
help_text='Newsletter subscription preference'
)
# forms.py - File handling fields
from django import forms
from django.core.validators import FileExtensionValidator
class FileFieldsForm(forms.Form):
"""File and image field examples"""
# Basic file field
document = forms.FileField(
help_text='Upload a document (PDF, DOC, DOCX)',
validators=[
FileExtensionValidator(
allowed_extensions=['pdf', 'doc', 'docx'],
message='Only PDF, DOC, and DOCX files are allowed.'
)
]
)
# Image field with validation
profile_picture = forms.ImageField(
required=False,
help_text='Upload a profile picture (JPG, PNG, GIF)',
widget=forms.FileInput(attrs={
'accept': 'image/*',
'class': 'form-control'
})
)
# Multiple file upload
attachments = forms.FileField(
widget=forms.ClearableFileInput(attrs={
'multiple': True,
'class': 'form-control'
}),
required=False,
help_text='Upload multiple files'
)
# File field with size validation
def clean_document(self):
document = self.cleaned_data.get('document')
if document:
# Check file size (5MB limit)
if document.size > 5 * 1024 * 1024:
raise forms.ValidationError('File size cannot exceed 5MB.')
# Check file type by content
if document.content_type not in ['application/pdf', 'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document']:
raise forms.ValidationError('Invalid file type.')
return document
def clean_profile_picture(self):
image = self.cleaned_data.get('profile_picture')
if image:
# Check image dimensions
if image.width > 1920 or image.height > 1080:
raise forms.ValidationError('Image dimensions cannot exceed 1920x1080 pixels.')
# Check file size (2MB limit for images)
if image.size > 2 * 1024 * 1024:
raise forms.ValidationError('Image size cannot exceed 2MB.')
return image
# forms.py - Widget customization examples
from django import forms
class WidgetCustomizationForm(forms.Form):
"""Examples of widget customization"""
# TextInput with custom attributes
username = forms.CharField(
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Enter username',
'autocomplete': 'username',
'maxlength': '30',
'pattern': '[a-zA-Z0-9_]+',
'title': 'Username can only contain letters, numbers, and underscores'
})
)
# Textarea with custom size and attributes
message = forms.CharField(
widget=forms.Textarea(attrs={
'rows': 5,
'cols': 40,
'class': 'form-control',
'placeholder': 'Enter your message here...',
'style': 'resize: vertical;',
'maxlength': '1000'
})
)
# Select with custom styling
country = forms.ChoiceField(
choices=[
('', 'Select Country'),
('us', 'United States'),
('ca', 'Canada'),
('uk', 'United Kingdom'),
('au', 'Australia'),
],
widget=forms.Select(attrs={
'class': 'form-select',
'data-placeholder': 'Choose a country'
})
)
# CheckboxSelectMultiple with custom layout
interests = forms.MultipleChoiceField(
choices=[
('sports', 'Sports'),
('music', 'Music'),
('travel', 'Travel'),
('technology', 'Technology'),
],
widget=forms.CheckboxSelectMultiple(attrs={
'class': 'form-check-input'
}),
required=False
)
# RadioSelect with custom styling
gender = forms.ChoiceField(
choices=[
('M', 'Male'),
('F', 'Female'),
('O', 'Other'),
],
widget=forms.RadioSelect(attrs={
'class': 'form-check-input'
}),
required=False
)
# Date input with custom format
birth_date = forms.DateField(
widget=forms.DateInput(attrs={
'type': 'date',
'class': 'form-control',
'max': '2023-12-31'
})
)
# Password input with strength indicator
password = forms.CharField(
widget=forms.PasswordInput(attrs={
'class': 'form-control',
'placeholder': 'Enter password',
'data-strength-meter': 'true',
'autocomplete': 'new-password'
})
)
# Number input with range
rating = forms.IntegerField(
widget=forms.NumberInput(attrs={
'type': 'range',
'min': '1',
'max': '10',
'step': '1',
'class': 'form-range',
'oninput': 'this.nextElementSibling.value = this.value'
})
)
# Color picker
theme_color = forms.CharField(
widget=forms.TextInput(attrs={
'type': 'color',
'class': 'form-control form-control-color'
}),
initial='#007bff'
)
# File input with preview
avatar = forms.ImageField(
widget=forms.FileInput(attrs={
'class': 'form-control',
'accept': 'image/*',
'onchange': 'previewImage(this)'
}),
required=False
)
# widgets.py - Custom widget implementations
from django import forms
from django.forms.widgets import Widget
from django.html import format_html
from django.utils.safestring import mark_safe
class RangeSliderWidget(Widget):
"""Custom range slider with value display"""
def __init__(self, attrs=None, min_value=0, max_value=100, step=1):
self.min_value = min_value
self.max_value = max_value
self.step = step
super().__init__(attrs)
def render(self, name, value, attrs=None, renderer=None):
if value is None:
value = self.min_value
final_attrs = self.build_attrs(attrs, {
'type': 'range',
'name': name,
'min': self.min_value,
'max': self.max_value,
'step': self.step,
'value': value,
'oninput': f'document.getElementById("{name}_display").textContent = this.value'
})
return format_html(
'<input{} /><span id="{}_display" class="range-value">{}</span>',
forms.utils.flatatt(final_attrs),
name,
value
)
class TagInputWidget(Widget):
"""Custom tag input widget"""
def render(self, name, value, attrs=None, renderer=None):
if value is None:
value = ''
final_attrs = self.build_attrs(attrs, {
'type': 'text',
'name': name,
'value': value,
'data-role': 'tagsinput',
'placeholder': 'Add tags...'
})
return format_html('<input{} />', forms.utils.flatatt(final_attrs))
class RichTextWidget(Widget):
"""Rich text editor widget"""
def render(self, name, value, attrs=None, renderer=None):
if value is None:
value = ''
final_attrs = self.build_attrs(attrs, {
'name': name,
'id': f'id_{name}',
'class': 'rich-text-editor'
})
html = format_html(
'<textarea{}>{}</textarea>',
forms.utils.flatatt(final_attrs),
value
)
# Add JavaScript for rich text editor
js = format_html(
'<script>initRichTextEditor("id_{}");</script>',
name
)
return mark_safe(html + js)
class DateTimePickerWidget(Widget):
"""Custom datetime picker widget"""
def __init__(self, attrs=None, format=None):
self.format = format or '%Y-%m-%d %H:%M'
super().__init__(attrs)
def render(self, name, value, attrs=None, renderer=None):
if value:
if hasattr(value, 'strftime'):
value = value.strftime(self.format)
else:
value = ''
final_attrs = self.build_attrs(attrs, {
'type': 'text',
'name': name,
'value': value,
'data-datetime-picker': 'true',
'placeholder': 'YYYY-MM-DD HH:MM'
})
return format_html('<input{} />', forms.utils.flatatt(final_attrs))
# forms.py - Using custom widgets
class CustomWidgetForm(forms.Form):
"""Form using custom widgets"""
priority = forms.IntegerField(
widget=RangeSliderWidget(min_value=1, max_value=10),
help_text='Select priority level'
)
tags = forms.CharField(
widget=TagInputWidget(attrs={'class': 'form-control'}),
help_text='Enter tags separated by commas',
required=False
)
content = forms.CharField(
widget=RichTextWidget(attrs={'rows': 10}),
help_text='Enter rich text content'
)
scheduled_time = forms.DateTimeField(
widget=DateTimePickerWidget(),
help_text='Select date and time'
)
# forms.py - Dynamic field creation
from django import forms
class DynamicFieldForm(forms.Form):
"""Form with dynamically generated fields"""
def __init__(self, field_configs=None, *args, **kwargs):
super().__init__(*args, **kwargs)
if field_configs:
for config in field_configs:
field_type = config.get('type', 'CharField')
field_name = config.get('name')
field_label = config.get('label', field_name.title())
field_required = config.get('required', True)
field_help_text = config.get('help_text', '')
# Create field based on type
if field_type == 'CharField':
field = forms.CharField(
label=field_label,
required=field_required,
help_text=field_help_text,
max_length=config.get('max_length', 100)
)
elif field_type == 'EmailField':
field = forms.EmailField(
label=field_label,
required=field_required,
help_text=field_help_text
)
elif field_type == 'IntegerField':
field = forms.IntegerField(
label=field_label,
required=field_required,
help_text=field_help_text,
min_value=config.get('min_value'),
max_value=config.get('max_value')
)
elif field_type == 'ChoiceField':
choices = config.get('choices', [])
field = forms.ChoiceField(
label=field_label,
required=field_required,
help_text=field_help_text,
choices=choices
)
elif field_type == 'BooleanField':
field = forms.BooleanField(
label=field_label,
required=field_required,
help_text=field_help_text
)
else:
# Default to CharField
field = forms.CharField(
label=field_label,
required=field_required,
help_text=field_help_text
)
# Add field to form
self.fields[field_name] = field
# Usage example
field_configs = [
{
'name': 'first_name',
'type': 'CharField',
'label': 'First Name',
'required': True,
'max_length': 50
},
{
'name': 'age',
'type': 'IntegerField',
'label': 'Age',
'required': True,
'min_value': 0,
'max_value': 150
},
{
'name': 'country',
'type': 'ChoiceField',
'label': 'Country',
'required': True,
'choices': [('us', 'USA'), ('ca', 'Canada'), ('uk', 'UK')]
}
]
# Create form with dynamic fields
dynamic_form = DynamicFieldForm(field_configs=field_configs)
# forms.py - Conditional field visibility
from django import forms
class ConditionalFieldForm(forms.Form):
"""Form with conditional field display"""
user_type = forms.ChoiceField(
choices=[
('individual', 'Individual'),
('business', 'Business'),
],
widget=forms.RadioSelect(attrs={
'onchange': 'toggleFields(this.value)'
})
)
# Individual fields
first_name = forms.CharField(
max_length=50,
widget=forms.TextInput(attrs={
'class': 'form-control individual-field'
}),
required=False
)
last_name = forms.CharField(
max_length=50,
widget=forms.TextInput(attrs={
'class': 'form-control individual-field'
}),
required=False
)
# Business fields
company_name = forms.CharField(
max_length=100,
widget=forms.TextInput(attrs={
'class': 'form-control business-field'
}),
required=False
)
tax_id = forms.CharField(
max_length=20,
widget=forms.TextInput(attrs={
'class': 'form-control business-field'
}),
required=False
)
def clean(self):
cleaned_data = super().clean()
user_type = cleaned_data.get('user_type')
# Make fields required based on user type
if user_type == 'individual':
if not cleaned_data.get('first_name'):
self.add_error('first_name', 'First name is required for individuals.')
if not cleaned_data.get('last_name'):
self.add_error('last_name', 'Last name is required for individuals.')
elif user_type == 'business':
if not cleaned_data.get('company_name'):
self.add_error('company_name', 'Company name is required for businesses.')
if not cleaned_data.get('tax_id'):
self.add_error('tax_id', 'Tax ID is required for businesses.')
return cleaned_data
Django's built-in fields and widgets provide a comprehensive foundation for creating sophisticated forms. Understanding the available field types, widget customization options, and advanced patterns enables you to build user-friendly interfaces that handle diverse data types and user interactions while maintaining consistency and accessibility.
Form Validation
Django's form validation system provides multiple layers of data validation, from field-level checks to complex form-wide business rules. Understanding validation patterns enables you to build robust forms that ensure data integrity and provide meaningful user feedback.
Form Rendering in Templates
Django provides flexible options for rendering forms in templates, from automatic rendering to complete manual control. Understanding these rendering techniques enables you to create consistent, accessible, and visually appealing forms.