Lesson 19 / 26
Forms and Validation
ModelForm with custom rules.
is_valid, cleaned_data, errors
Django forms validate and convert input: field types check formats, ModelForm builds fields from a model (including foreign key choices), and clean_<field> methods add custom rules. is_valid() runs all checks; cleaned_data holds converted values and errors maps fields to messages for display. Forms also render HTML and work with CSRF protection in templates.
The product form
I ran this with Django 6.1.1 and Python 3.12 in a demo project (shopsite with a catalog app) using SQLite. A ModelForm for three fields with a custom positive-price rule.
from django import forms
from .models import Product
class ProductForm(forms.ModelForm):
class Meta:
model = Product
fields = ["category", "name", "price"]
def clean_price(self):
price = self.cleaned_data["price"]
if price <= 0:
raise forms.ValidationError("Price must be positive.")
return priceValidating three submissions, run
I ran this script with python manage.py shell --no-imports in the demo project (Django 6.1.1, SQLite). A complete submission is valid; an empty name and negative price fail with both messages; an unknown category id and non-numeric price are rejected by the field types.
from catalog.forms import ProductForm
from catalog.models import Category
office = Category.objects.get(name="Office")
for data in [{"category": office.pk, "name": "Stapler", "price": "250"},
{"category": office.pk, "name": "", "price": "-1"},
{"category": 999, "name": "Clip", "price": "abc"}]:
form = ProductForm(data)
print(form.is_valid(), {k: [str(m) for m in v] for k, v in form.errors.items()})
Output:
True {}
False {'name': ['This field is required.'], 'price': ['Price must be positive.']}
False {'category': ['Select a valid choice. That choice is not one of the available choices.'], 'price': ['Enter a number.']}Quick check: Where do you add a custom validation rule for one field in a Django form?
- In the migration file
- In urls.py
- In settings.py
- In a clean_<fieldname> method
Answer
In a clean_<fieldname> method — Field-level clean methods.