Introduction to ModelForms
ModelForms save you from writing repetitive form code. They automatically generate form fields from your model's fields.
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'content', 'published']
The Meta class tells Django which model to use and which fields to include in the form.
Customizing Widgets and Labels
You can customize how fields render by specifying widgets, labels, and help text.
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'content']
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control'}),
'content': forms.Textarea(attrs={'rows': 10}),
}
labels = {
'title': 'Post Title',
}
help_text = {
'content': 'Write your post content here.',
}
Widgets control the HTML element used for each field. Labels and help text appear in the rendered form.
Saving Form Data
ModelForms have a convenient save() method that creates or updates model instances.
def create_post(request):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
post = form.save()
return redirect('post_detail', pk=post.pk)
else:
form = PostForm()
return render(request, 'post_form.html', {'form': form})
The save() method returns the new model instance. You can also call save(commit=False) to get an unsaved instance.
Using commit=False
Sometimes you need to set additional fields before saving. Use commit=False to get an unsaved instance.
def create_post(request):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
return redirect('post_detail', pk=post.pk)
else:
form = PostForm()
return render(request, 'post_form.html', {'form': form})
This pattern is common when you need to set fields that aren't in the form, like the current user or a calculated value.
Try it Yourself โOverriding clean Methods
You can add custom validation by overriding clean_ or the clean() method.
class EventForm(forms.ModelForm):
class Meta:
model = Event
fields = ['name', 'start_date', 'end_date']
def clean(self):
cleaned_data = super().clean()
start = cleaned_data.get('start_date')
end = cleaned_data.get('end_date')
if start and end and start > end:
raise forms.ValidationError('End date must be after start date.')
return cleaned_data
The clean() method lets you validate relationships between multiple fields. Always call super().clean() first.