# Forms and Validation — Django / FastAPI

Source: https://www.skillbyai.com/en/django-fastapi/v-forms

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

```python
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.

```python
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.']}
```

**Quiz:** 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
- [x] In a clean_<fieldname> method

*Answer:* In a clean_<fieldname> method. Field-level clean methods.
