Model schema¶
Generate Pydantic schemas from Django model definitions, reducing boilerplate for CRUD resources.
ModelSchema¶
Define a Meta inner class to configure field generation:
from django_ninja_jsonapi import ModelSchema
class ArticleSchema(ModelSchema):
class Meta:
model = Article
fields = ["uuid", "title", "body", "status", "organization_name", "created_dt"]
resource_type = "articles"
id_field = "uuid"
class ArticleCreateSchema(ModelSchema):
class Meta:
model = Article
fields = ["title", "body", "status"]
optional_fields = {"status"}
class ArticleUpdateSchema(ModelSchema):
class Meta:
model = Article
fields = ["title", "body", "status"]
all_optional = True
Extra fields and overrides¶
Declare fields directly on the class body — they merge with auto-generated fields:
class ArticleSchema(ModelSchema):
full_address: str = "" # extra field not on the model
title: str # override: make required even if model has default
class Meta:
model = Article
fields = ["id", "title"]
resource_type = "articles"
User-declared fields always take precedence over auto-generated ones.
Meta options¶
| Option | Type | Default | Description |
|---|---|---|---|
model |
Django Model | required | The Django model class |
fields |
list[str] |
None |
Field names to include. Supports DB fields and @property names. None means all concrete model fields. |
exclude |
set[str] |
None |
Field names to exclude |
resource_type |
str |
None |
JSON:API resource type. When set, attaches JsonApiMeta so the schema works with NinjaJsonAPI. |
id_field |
str |
None |
Name of the field used as the JSON:API id. |
all_optional |
bool |
False |
Make all fields Optional (for PATCH) |
optional_fields |
set[str] |
None |
Specific fields to make Optional |
Features¶
Automatic type mapping¶
Django field types are automatically mapped to Python types:
| Django Field | Python Type |
|---|---|
CharField, TextField, EmailField, URLField, SlugField |
str |
IntegerField, BigIntegerField, SmallIntegerField, AutoField |
int |
FloatField |
float |
BooleanField |
bool |
DateField |
datetime.date |
DateTimeField |
datetime.datetime |
UUIDField |
uuid.UUID |
DecimalField |
Decimal |
JSONField |
Any |
ForeignKey |
type of related model's PK |
Nullable and default fields¶
Fields with null=True or a default= value are automatically made Optional.
@property fields¶
You can include @property names in the fields list:
class Article(models.Model):
title = models.CharField(max_length=200)
organization = models.ForeignKey(Organization, on_delete=models.CASCADE)
@property
def organization_name(self) -> str:
return self.organization.name
class ArticleSchema(ModelSchema):
class Meta:
model = Article
fields = ["id", "title", "organization_name"]
from_attributes enabled¶
All generated schemas have ConfigDict(from_attributes=True), so they work with schema.model_validate(instance, from_attributes=True) and the renderer's schema-aware coercion.
Extra fields¶
Add fields not on the model by declaring them on the class body:
class ArticleSchema(ModelSchema):
custom_score: float = 0.0
class Meta:
model = Article
fields = ["title"]
Full example¶
from django_ninja_jsonapi import NinjaJsonAPI, ModelSchema, apply_attributes, jsonapi_paginate
# Schemas
class ArticleSchema(ModelSchema):
class Meta:
model = Article
fields = ["uuid", "title", "body", "status", "created_dt"]
resource_type = "articles"
id_field = "uuid"
class ArticleCreateSchema(ModelSchema):
class Meta:
model = Article
fields = ["title", "body"]
class ArticleUpdateSchema(ModelSchema):
class Meta:
model = Article
fields = ["title", "body", "status"]
all_optional = True
# API
api = NinjaJsonAPI()
@api.get("/articles", response=list[ArticleSchema])
def list_articles(request):
return jsonapi_paginate(request, Article.objects.order_by("id"))
@api.post("/articles", response={201: ArticleSchema})
def create_article(request, body: ArticleCreateSchema):
article = Article.objects.create(**body.model_dump())
return article
@api.patch("/articles/{article_id}", response=ArticleSchema)
def update_article(request, article_id: str, body: ArticleUpdateSchema):
article = Article.objects.get(uuid=article_id)
apply_attributes(article, body)
return article