Django forms from Pydantic models
django-schemaform lets you define your form schema once using Pydantic and get a fully-featured Django form with automatic field mapping, constraints, and validation.
Installation
uv add django-schemaform
Or with pip:
pip install django-schemaform
Add the images extra if your schemas use ImageUpload, which needs Pillow:
uv add "django-schemaform[images]"
Quick Start
Define a Pydantic model and create a form:
from pydantic import BaseModel, EmailStr, Field
from schemaform import SchemaForm
class ContactSchema(BaseModel):
name: str = Field(min_length=2, max_length=100)
email: EmailStr
message: str = Field(min_length=10, description="Your message to us")
class ContactForm(SchemaForm):
class Meta:
schema = ContactSchema
Use it in your Django view:
from django.shortcuts import render, redirect
def contact_view(request):
if request.method == "POST":
form = ContactForm(request.POST)
if form.is_valid():
data = form.cleaned_data # dict with Pydantic-validated values
# process data...
render(request, "success.html")
else:
form = ContactForm()
return render(request, "contact.html", {"form": form})
That's it! The form automatically:
- Maps
str→CharField,EmailStr→EmailField - Applies
min_length/max_lengthconstraints - Uses the
descriptionas help text - Validates through Pydantic on submit
Field Type Mapping
Core Python Types
| Python Type | Django Field |
|---|---|
str |
CharField |
int |
IntegerField |
float |
FloatField |
bool |
BooleanField |
Decimal |
DecimalField |
date |
DateField |
time |
TimeField |
datetime |
DateTimeField |
timedelta |
DurationField |
UUID |
UUIDField |
Pydantic Types
| Pydantic Type | Django Field |
|---|---|
EmailStr |
EmailField |
HttpUrl |
URLField |
AnyUrl |
URLField |
SecretStr |
CharField (password widget) |
PastDate |
DateField |
FutureDate |
DateField |
PastDatetime |
DateTimeField |
FutureDatetime |
DateTimeField |
File Upload Types
| SchemaForm Type | Django Field |
|---|---|
FileUpload |
FileField |
ImageUpload |
ImageField |
from schemaform import SchemaForm, FileUpload, ImageUpload
class UploadSchema(BaseModel):
document: FileUpload
photo: ImageUpload | None = None # Optional
ImageUpload maps to Django's ImageField, which requires Pillow. Install it with the images extra:
uv add "django-schemaform[images]"
Choice Fields
| Python Type | Django Field |
|---|---|
Literal["a", "b", "c"] |
ChoiceField |
enum.Enum subclass |
ChoiceField |
from typing import Literal
from enum import Enum
class Priority(str, Enum):
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
class TaskSchema(BaseModel):
status: Literal["pending", "done"]
priority: Priority
category: Priority | None = None # Optional - adds empty choice
Constraint Mapping
| Pydantic Constraint | Django Field Attribute |
|---|---|
min_length |
min_length |
max_length |
max_length |
ge / gt |
min_value |
le / lt |
max_value |
max_digits |
max_digits (Decimal) |
decimal_places |
decimal_places (Decimal) |
multiple_of |
step (widget attribute) |
pattern |
enforced by Pydantic (no Django validator) |
Labels and Help Text
Field metadata from Pydantic is automatically applied:
class ProfileSchema(BaseModel):
username: str = Field(
title="Username", # → label
description="Choose wisely" # → help_text
)
Validation
Single-Field Validation
Use Pydantic's @field_validator for field-level validation:
from pydantic import BaseModel, field_validator
from schemaform import SchemaForm, FileUpload
class UploadSchema(BaseModel):
resume: FileUpload
@field_validator("resume")
@classmethod
def validate_resume(cls, v):
if v.size > 5 * 1024 * 1024: # 5MB
raise ValueError("File must be under 5MB")
if not v.content_type == "application/pdf":
raise ValueError("Only PDF files allowed")
return v
Cross-Field Validation
Use @model_validator for validation across multiple fields:
from pydantic import BaseModel, SecretStr, model_validator
from schemaform import SchemaForm
class RegistrationSchema(BaseModel):
email: EmailStr
password: SecretStr = Field(min_length=8)
password_confirm: SecretStr
@model_validator(mode="after")
def passwords_match(self):
if self.password.get_secret_value() != self.password_confirm.get_secret_value():
raise ValueError("Passwords do not match")
return self
class RegistrationForm(SchemaForm):
class Meta:
schema = RegistrationSchema
Validation errors are automatically mapped to the appropriate form fields or to __all__ for non-field errors.
Demo Application
A demo Django application is included with example forms showcasing various features:
- Contact Form — Email, time fields, Literal choices
- User Registration — SecretStr passwords, PastDate, password matching validation
- Event Booking — Date/time, Decimal constraints, Enum choices, cross-field validation
- Product Review — UUID, integer rating with range, optional ImageUpload
- Job Application — FileUpload with validation, HttpUrl, Enum, Decimal
- Medical Appointment — FutureDatetime, Enum, sensitive data handling
See demo/README.md for setup instructions.
Requirements
- Python ≥ 3.12
- Django ≥ 5.2, < 6.0
- Pydantic ≥ 2.0, < 3.0
- annotated-types ≥ 0.6
- Pillow — only for
ImageUpload, via theimagesextra
Contributing
Contributions are welcome! See CONTRIBUTING.md for development setup and guidelines.
License
BSD-3-Clause — see LICENSE.md for details.
Metadata
Release files for django-schemaform 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| django_schemaform-0.2.0.tar.gz | 26.4 kB | Without provenance | Details for django_schemaform-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| django_schemaform-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for django_schemaform-0.2.0-py3-none-any.whl |
Total release size: 40.0 kB
Release files / django_schemaform-0.2.0.tar.gz
| Download URL | django_schemaform-0.2.0.tar.gz |
|---|---|
| Size | 26.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5d6558f26ff95df4e1bed6eab961e4b1a96047fa059b014456222ee7580112bb
|
|
BLAKE2b-256 checksum How to use checksums |
d18f48e936619f6bf38e89d36b817235819c4e6f23f645152d892f0730d6c24d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / django_schemaform-0.2.0-py3-none-any.whl
| Download URL | django_schemaform-0.2.0-py3-none-any.whl |
|---|---|
| Size | 13.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1ea916a545d6ad373329f5b3aa4bba263ab64efd8482b8efc2456c53aaecc091
|
|
BLAKE2b-256 checksum How to use checksums |
c99c0f4bae017ed2e25a5b4ec281e129d3f5cfc8946797314e81db29bb33f4d3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for django-schemaform 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.