Django Ninja AIO CRUD - Rest Framework
Project description
Async CRUD framework for Django Ninja
Automatic schema generation · Filtering · Pagination · Auth · M2M management
Documentation · PyPI · Framework Comparison · Performance Benchmarks · Example Project · Issues
Features
| Feature | Description | |
|---|---|---|
| 🔒 Type Safety | Generic classes | Full IDE autocomplete and type checking with generic ModelUtil, Serializer, and APIViewSet |
| Meta-driven Serializer | Dynamic schemas | Generate CRUD schemas for existing Django models without changing base classes |
| Async CRUD ViewSets | Full operations | Create, list, retrieve, update, delete — all async |
| Auto Schemas | Pydantic generation | Automatic read/create/update schemas from ModelSerializer |
| Dynamic Query Params | Runtime schemas | Built with pydantic.create_model for flexible filtering |
| Per-method Auth | Granular control | auth, get_auth, post_auth, etc. |
| Async Pagination | Customizable | Fully async, pluggable pagination classes |
| M2M Relations | Add/remove/list | Endpoints via M2MRelationSchema with filtering support |
| Reverse Relations | Nested serialization | Automatic handling of reverse FK and M2M |
| Lifecycle Hooks | Extensible | before_save, after_save, custom_actions, on_delete, and more |
| Schema Validators | Pydantic validators | @field_validator and @model_validator on serializer classes |
| ORJSON Renderer | Performance | Built-in fast JSON rendering via NinjaAIO |
Quick Start
Option A: Meta-driven Serializer (existing models)
Use this if you already have Django models and don't want to change their base class.
from ninja_aio.models import serializers
from ninja_aio.views import APIViewSet
from ninja_aio import NinjaAIO
from . import models
class BookSerializer(serializers.Serializer):
class Meta:
model = models.Book
schema_in = serializers.SchemaModelConfig(fields=["title", "published"])
schema_out = serializers.SchemaModelConfig(fields=["id", "title", "published"])
schema_update = serializers.SchemaModelConfig(
optionals=[("title", str), ("published", bool)]
)
api = NinjaAIO()
@api.viewset(models.Book)
class BookViewSet(APIViewSet):
serializer_class = BookSerializer
Option B: ModelSerializer (new projects)
Define models with built-in serialization for minimal boilerplate.
models.py
from django.db import models
from ninja_aio.models import ModelSerializer
class Book(ModelSerializer):
title = models.CharField(max_length=120)
published = models.BooleanField(default=True)
class ReadSerializer:
fields = ["id", "title", "published"]
class CreateSerializer:
fields = ["title", "published"]
class UpdateSerializer:
optionals = [("title", str), ("published", bool)]
views.py
from ninja_aio import NinjaAIO
from ninja_aio.views import APIViewSet
from .models import Book
api = NinjaAIO()
@api.viewset(Book)
class BookViewSet(APIViewSet):
pass
Visit
/docs— CRUD endpoints ready.
Query Filtering
@api.viewset(Book)
class BookViewSet(APIViewSet):
query_params = {"published": (bool, None), "title": (str, None)}
async def query_params_handler(self, queryset, filters):
if filters.get("published") is not None:
queryset = queryset.filter(published=filters["published"])
if filters.get("title"):
queryset = queryset.filter(title__icontains=filters["title"])
return queryset
GET /book/?published=true&title=python
Many-to-Many Relations
from ninja_aio.schemas import M2MRelationSchema
class Tag(ModelSerializer):
name = models.CharField(max_length=50)
class ReadSerializer:
fields = ["id", "name"]
class Article(ModelSerializer):
title = models.CharField(max_length=120)
tags = models.ManyToManyField(Tag, related_name="articles")
class ReadSerializer:
fields = ["id", "title", "tags"]
@api.viewset(Article)
class ArticleViewSet(APIViewSet):
m2m_relations = [
M2MRelationSchema(
model=Tag,
related_name="tags",
filters={"name": (str, "")}
)
]
async def tags_query_params_handler(self, queryset, filters):
n = filters.get("name")
if n:
queryset = queryset.filter(name__icontains=n)
return queryset
Endpoints:
GET /article/{pk}/tag?name=dev
POST /article/{pk}/tag/ body: {"add": [1, 2], "remove": [3]}
Authentication (JWT)
from ninja_aio.auth import AsyncJwtBearer
from joserfc import jwk
class JWTAuth(AsyncJwtBearer):
jwt_public = jwk.RSAKey.import_key("-----BEGIN PUBLIC KEY----- ...")
jwt_alg = "RS256"
claims = {"sub": {"essential": True}}
async def auth_handler(self, request):
book_id = self.dcd.claims.get("sub")
return await Book.objects.aget(id=book_id)
@api.viewset(Book)
class SecureBookViewSet(APIViewSet):
auth = [JWTAuth()]
get_auth = None # list/retrieve remain public
Lifecycle Hooks
Available on every save/delete cycle:
| Hook | When |
|---|---|
on_create_before_save |
Before first save |
on_create_after_save |
After first save |
before_save |
Before any save |
after_save |
After any save |
on_delete |
After deletion |
custom_actions(payload) |
Create/update custom field logic |
post_create() |
After create commit |
Custom Endpoints
from ninja_aio.decorators import api_get
@api.viewset(Book)
class BookViewSet(APIViewSet):
@api_get("/stats/")
async def stats(self, request):
total = await Book.objects.acount()
return {"total": total}
Pagination
Default: PageNumberPagination. Override per ViewSet:
from ninja.pagination import PageNumberPagination
class LargePagination(PageNumberPagination):
page_size = 50
max_page_size = 200
@api.viewset(Book)
class BookViewSet(APIViewSet):
pagination_class = LargePagination
Schema Validators
Add Pydantic @field_validator and @model_validator directly on serializer classes for input validation.
ModelSerializer
Declare validators on inner serializer classes:
from django.db import models
from pydantic import field_validator, model_validator
from ninja_aio.models import ModelSerializer
class Book(ModelSerializer):
title = models.CharField(max_length=120)
description = models.TextField(blank=True)
class CreateSerializer:
fields = ["title", "description"]
@field_validator("title")
@classmethod
def validate_title_min_length(cls, v):
if len(v) < 3:
raise ValueError("Title must be at least 3 characters")
return v
class UpdateSerializer:
optionals = [("title", str), ("description", str)]
@field_validator("title")
@classmethod
def validate_title_not_empty(cls, v):
if v is not None and len(v.strip()) == 0:
raise ValueError("Title cannot be blank")
return v
class ReadSerializer:
fields = ["id", "title", "description"]
@model_validator(mode="after")
def enrich_output(self):
# Transform or enrich the output schema
return self
Meta-driven Serializer
Use dedicated {Type}Validators inner classes:
from pydantic import field_validator, model_validator
from ninja_aio.models import serializers
from . import models
class BookSerializer(serializers.Serializer):
class Meta:
model = models.Book
schema_in = serializers.SchemaModelConfig(fields=["title", "description"])
schema_out = serializers.SchemaModelConfig(fields=["id", "title", "description"])
schema_update = serializers.SchemaModelConfig(
optionals=[("title", str), ("description", str)]
)
class CreateValidators:
@field_validator("title")
@classmethod
def validate_title_min_length(cls, v):
if len(v) < 3:
raise ValueError("Title must be at least 3 characters")
return v
class UpdateValidators:
@field_validator("title")
@classmethod
def validate_title_not_empty(cls, v):
if v is not None and len(v.strip()) == 0:
raise ValueError("Title cannot be blank")
return v
class ReadValidators:
@model_validator(mode="after")
def enrich_output(self):
return self
Validator class mapping:
| Schema type | ModelSerializer | Serializer (Meta-driven) |
|---|---|---|
| Create | CreateSerializer |
CreateValidators |
| Update | UpdateSerializer |
UpdateValidators |
| Read | ReadSerializer |
ReadValidators |
| Detail | DetailSerializer |
DetailValidators |
Disable Operations
@api.viewset(Book)
class ReadOnlyBookViewSet(APIViewSet):
disable = ["update", "delete"]
Framework Comparison
How does Django Ninja AIO compare to other Python REST frameworks? We benchmark against Django Ninja, ADRF, and FastAPI — focusing on complex async operations like reverse FK and M2M serialization.
| Framework | Lines of Code | Reverse FK Handling | Auto Prefetch | CRUD Automation |
|---|---|---|---|---|
| Django Ninja AIO | ~20 | Automatic | Yes | Full |
| FastAPI | ~80+ | Manual async iteration | No | None |
| ADRF | ~45+ | Needs serializer config | Manual | Partial |
View Full Comparison — Code examples, benchmark results, and interactive charts
Performance
View live benchmarks tracking schema generation, serialization, and CRUD throughput:
Live Performance Report — Interactive charts with historical trends
Performance Tips
- Use
queryset_requestclassmethod toselect_related/prefetch_related - Index frequently filtered fields
- Keep pagination enabled for large datasets
- Limit slices (
queryset = queryset[:1000]) for heavy searches
Contributing
- Fork the repository
- Create a feature branch
- Add tests for your changes
- Run lint:
ruff check . - Open a Pull Request
Support
If you find this project useful, consider giving it a star or supporting development:
License
MIT License. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_ninja_aio_crud-2.23.0.tar.gz.
File metadata
- Download URL: django_ninja_aio_crud-2.23.0.tar.gz
- Upload date:
- Size: 2.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: python-requests/2.31.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
564b42363294995ebc7ae5b5985337142bd8bc4765b4b19c808bcfb03df79d27
|
|
| MD5 |
3d288fd2647ea6831918b50e238aebfe
|
|
| BLAKE2b-256 |
d51ace87ace6e1d200d09189209f29e07b48071136639abaaf834f70e1c0589d
|
File details
Details for the file django_ninja_aio_crud-2.23.0-py3-none-any.whl.
File metadata
- Download URL: django_ninja_aio_crud-2.23.0-py3-none-any.whl
- Upload date:
- Size: 67.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: python-requests/2.31.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
96607abd9c9b822ef8359e4c7b03e8afaf038b05028ba3fe3bc84c99b7777343
|
|
| MD5 |
5f5d3073773e1b0de1c8fc98d323fdf5
|
|
| BLAKE2b-256 |
c3b01b6c1bf00c50fb2544a58070e4a2f05e7f5ceea01908c2537cef54998f1a
|