Skip to main content

Django Modern Schemas

django-modern-schemas generates Pydantic schemas from Django ORM models. It reuses model types, constraints, defaults, choices, and supported relationships to reduce duplication between the data and validation/serialization layers. The library exposes ModelSchema, Schema, and SchemaFactory to define or generate these schemas. It is maintained by Open Byte.

📚 Full documentation: open-byte.github.io/django-modern-schemas

PyPI Python versions License Latest on Django Packages

Requirements

  • Python 3.10 or newer
  • Django 3.2 or newer
  • Pydantic 2.12 or newer

Quick start

pip install django-modern-schemas

Nothing goes into INSTALLED_APPS — schemas are ordinary Python classes.

Start from a model you already have — models.py:

from django.db import models


class Author(models.Model):
    name = models.CharField(max_length=100)


class Article(models.Model):
    title = models.CharField(max_length=120)
    body = models.TextField(blank=True, default='')
    views = models.PositiveIntegerField(default=0)
    published = models.BooleanField(default=False)
    author = models.ForeignKey(Author, on_delete=models.CASCADE)

Point a schema at it. The fields, their types, their constraints, and their defaults are read from the model — you restate none of them in schemas.py:

from django_modern_schemas import ModelSchema

from .models import Article


class ArticleSchema(ModelSchema[Article]):
    class Config:
        model = Article

ModelSchema is generic in its model. The parameter is free at runtime and makes create(), update(), and save() typed as returning Article, so your type checker follows the value all the way into the rest of the view.

>>> list(ArticleSchema.model_fields)
['id', 'title', 'body', 'views', 'published', 'author']

Validate input

max_length=120 was declared once, on the column, and it is enforced before any SQL runs. ValidationError.errors() is already shaped like a 400 response body:

>>> ArticleSchema.model_validate({'title': 'x' * 200, 'author': 1})
Traceback (most recent call last):
    ...
pydantic_core._pydantic_core.ValidationError: 1 validation error for ArticleSchema
title
  String should have at most 120 characters [type=string_too_long, ...]

Write it to the database

>>> author = Author.objects.create(name='Ada Lovelace')
>>> article = ArticleSchema.model_validate(
...     {'title': 'Schemas from models', 'body': 'One source of truth.', 'author': author.pk}
... ).create()
>>> article.pk is not None
True

Serialize it back out

The same class reads a Django instance, so one schema covers both directions:

>>> ArticleSchema.model_validate(article).model_dump()
{'id': 1, 'title': 'Schemas from models', 'body': 'One source of truth.', 'views': 0, 'published': False, 'author': 1}
>>> ArticleSchema.model_validate(article).model_dump_json()
'{"id":1,"title":"Schemas from models","body":"One source of truth.","views":0,"published":false,"author":1}'

Round trip: read, edit, save

A schema validated from an instance stays bound to it, so save() updates that row instead of inserting a new one — no bookkeeping on your side:

>>> schema = ArticleSchema.model_validate(article)
>>> schema.title = 'Schemas from models, revisited'
>>> saved = schema.save()
>>> saved.pk == article.pk
True
>>> Article.objects.get(pk=article.pk).title
'Schemas from models, revisited'
>>> Article.objects.count()   # updated in place, not duplicated
1

PATCH endpoints

Mark fields optional and update with partial=True, and keys the client never sent are never written:

class ArticlePatchSchema(ModelSchema[Article]):
    class Config:
        model = Article
        fields = ['title', 'published']
        optional = ['title', 'published']
>>> Article.objects.filter(pk=article.pk).update(views=42)   # the article got some traffic
1
>>> article.refresh_from_db()
>>> ArticlePatchSchema.model_validate({'published': True}).update(article, partial=True).published
True
>>> Article.objects.get(pk=article.pk).views   # untouched by the patch
42

Without partial=True that same payload would write views back to its default and undo the count.

Publish the contract

model_json_schema() hands OpenAPI tooling a description generated from the model, maxLength and defaults included:

>>> ArticleSchema.model_json_schema()['required']
['title', 'author']
>>> ArticleSchema.model_json_schema()['properties']['title']['maxLength']
120

From here: Getting Started walks the same ground in more detail, and Relations covers foreign keys, many-to-many, and nesting with depth.

Documentation

The documentation site is published at open-byte.github.io/django-modern-schemas. It is built with Material for MkDocs, its source lives in docs, and every Python example on it is executed by the test suite — a drifting example fails the build.

uv sync --group docs
uv run --group docs mkdocs serve

Schema configuration

  • model: the Django model used to build the schema.
  • fields: fields exposed by the generated schema.
  • exclude: fields to omit from the generated schema.
  • optional: fields that should be optional.
  • depth: the nesting depth for supported related models.

Tutorials

Credits and acknowledgements

Django Modern Schemas is maintained by Open Byte.

This project is a new evolution of Ninja Schema and is developed with the original creator's permission.

Special thanks and full recognition go to Tochukwu (@eadwinCode), the creator of Ninja Schema and Django Ninja Extra. Thank you for the effort, design, and work invested in both libraries, and for granting permission to modify and create this new implementation so that the idea can continue. The original work is credited to him.

Inspired by: Django Ninja and djantic.

Metadata

Release files for django-modern-schemas 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-modern-schemas 0.1.1
File Size Uploaded
django_modern_schemas-0.1.1.tar.gz 18.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-modern-schemas 0.1.1
File Interpreter ABI Platform
django_modern_schemas-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 43.9 kB

Release files / django_modern_schemas-0.1.1.tar.gz

Download URL django_modern_schemas-0.1.1.tar.gz
Size 18.6 kB
Tags Source
SHA-256 checksum
How to use checksums
f1ef41e7415422447c621224fa37675465d9966cbd476a43c19f31c73e61bdc2
BLAKE2b-256 checksum
How to use checksums
b164fa3a29e51b8f4333c4790c658ae9121a54007b2611949e12efb912b3cef0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.6 {"installer":{"name":"uv","version":"0.12.6","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_modern_schemas-0.1.1-py3-none-any.whl

Download URL django_modern_schemas-0.1.1-py3-none-any.whl
Size 25.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3701447642f596c679457739b0ec36882c5cbda178f800e89de045c88ccc2a5f
BLAKE2b-256 checksum
How to use checksums
0a2355afa978424aad13ab17ccb8db280f6bf1706d2aea168c61b01865ceee58
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.6 {"installer":{"name":"uv","version":"0.12.6","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 history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page