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 price

Validating 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.