🥷 Django Ninja Aio CRUD¶
Django Ninja Aio CRUD is a powerful async REST framework built on top of Django Ninja. It provides automatic CRUD operations, class-based views, and built-in utilities to make API development faster and cleaner.
✨ Key Features¶
- 📦 Serializer (Meta-driven) — Define schemas for existing Django models without inheriting ModelSerializer
- 🚀 Fully Async - Built for Django's async ORM
- 🔄 Automatic CRUD - Generate complete REST APIs with minimal code
- 📝 ModelSerializer - Define schemas directly on models
- 🎯 Class-Based Views - Clean, organized view architecture
- 🔐 JWT Authentication - Built-in async JWT bearer authentication
- 📄 Auto Documentation - OpenAPI/Swagger UI out of the box
- 🔗 Relationship Support - Automatic nested serialization (FK, M2M, reverse relations)
- 📊 Pagination - Built-in async pagination support
- ⚡ Performance - Using
orjsonfor fast JSON serialization
🎯 Why Django Ninja Aio CRUD?¶
Traditional Django REST development requires:
- Separate serializer classes
- Manual CRUD view implementation
- Repetitive boilerplate code
- Complex relationship handling
Django Ninja Aio CRUD eliminates this complexity:
# schema.py
class UserSchemaOut(ModelSchema)
class Meta:
model = User
fields = ['id', 'username', 'email']
class UserSchemaIn(ModelSchema):
class Meta:
model = User
fields = ['username', 'email', 'password']
# views.py
@api.get("/users", response={200: list[UserSchemaOut]})
async def list_users(request):
return [user async for user in User.objects.select_related().all()]
@api.post("/users/", response={201: UserSchemaOut})
async def create_user(request, data: UserSchemaIn):
user = await User.objects.select_related().acreate(**data.model_dump())
return 201, user
# ... more views for retrieve, update, delete
# models.py
class User(ModelSerializer):
username = models.CharField(max_length=150)
email = models.EmailField()
password = models.CharField(max_length=128)
class ReadSerializer:
fields = ["id", "username", "email"]
class CreateSerializer:
fields = ["username", "email", "password"]
class UpdateSerializer:
optionals = [("email", str)]
# views.py
@api.viewset(User)
class UserViewSet(APIViewSet):
pass
# Done! List, Create, Retrieve, Update, Delete endpoints ready
📚 Documentation¶
Explore detailed documentation for each component:
Serializer (Meta-driven)¶
- Serializer (Meta-driven) - Dynamic schemas for existing Django models without inheriting ModelSerializer
Models¶
- Model Serializer - Schema generation and serialization
- Model Util - Async CRUD utilities
Views¶
- API View - Simple custom views
- API View Set - Complete CRUD operations
Advanced Topics¶
- Authentication - JWT and custom auth
- Pagination - Customize pagination behavior
Start with Serializer¶
Use Meta-driven Serializer first if you already have Django models and want immediate CRUD without changing bases:
from ninja_aio.models import serializers
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)])
````
Attach to a ViewSet:
```python
from ninja_aio.views import APIViewSet
from ninja_aio import NinjaAIO
api = NinjaAIO()
@api.viewset(models.Book)
class BookViewSet(APIViewSet):
serializer_class = BookSerializer
Query optimization and schemas¶
- Declare query optimizations on models via
class QuerySet(read, queryset_request, extras). - Use
QueryUtilto apply scope-basedselect_related/prefetch_related. - Standard query schemas:
ObjectsQuerySchema(filters=..., select_related=..., prefetch_related=...)ObjectQuerySchema(getters=..., select_related=..., prefetch_related=...)QuerySchema(filters=... | getters=...)
ViewSets internally use these to:
- Build optimized querysets in list/retrieve.
- Serialize via
list_read_sandread_s.
Example:
items = await ModelUtil(Article).list_read_s(
Article.generate_read_s(),
request,
query_data=ObjectsQuerySchema(filters={"category": 3}),
is_for_read=True,
)
💡 Example: Complete Blog API¶
Here's a real-world example with relationships:
# models.py
from django.db import models
from ninja_aio.models import ModelSerializer
class Author(ModelSerializer):
name = models.CharField(max_length=200)
email = models.EmailField(unique=True)
bio = models.TextField(blank=True)
class ReadSerializer:
fields = ["id", "name", "email", "bio", "articles"]
class CreateSerializer:
fields = ["name", "email"]
optionals = [("bio", str)]
class Category(ModelSerializer):
name = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
class ReadSerializer:
fields = ["id", "name", "slug"]
class CreateSerializer:
fields = ["name", "slug"]
class Article(ModelSerializer):
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
content = models.TextField()
author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name="articles")
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
tags = models.ManyToManyField('Tag', related_name="articles")
is_published = models.BooleanField(default=False)
views = models.IntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
class ReadSerializer:
fields = [
"id", "title", "slug", "content",
"author", "category", "tags",
"is_published", "views", "created_at"
]
class CreateSerializer:
fields = ["title", "slug", "content", "author", "category"]
customs = [("notify_subscribers", bool, True)]
class UpdateSerializer:
optionals = [
("title", str),
("content", str),
("is_published", bool),
]
async def custom_actions(self, payload: dict):
if payload.get("notify_subscribers"):
# Send notifications
await notify_new_article(self)
class Tag(ModelSerializer):
name = models.CharField(max_length=50, unique=True)
class ReadSerializer:
fields = ["id", "name"]
# views.py
from ninja_aio import NinjaAIO
from ninja_aio.views import APIViewSet
from .models import Author, Category, Article, Tag
api = NinjaAIO(title="Blog API", version="1.0.0")
@api.viewset(Author)
class AuthorViewSet(APIViewSet):
pass
@api.viewset(Category)
class CategoryViewSet(APIViewSet):
pass
@api.viewset(Article)
class ArticleViewSet(APIViewSet):
query_params = {
"is_published": (bool, None),
"category": (int, None),
"author": (int, None),
}
async def query_params_handler(self, queryset, filters):
if filters.get("is_published") is not None:
queryset = queryset.filter(is_published=filters["is_published"])
if filters.get("category"):
queryset = queryset.filter(category_id=filters["category"])
if filters.get("author"):
queryset = queryset.filter(author_id=filters["author"])
return queryset
@api.viewset(Tag)
class TagViewSet(APIViewSet):
pass
This creates a complete blog API with:
- 4 models with relationships
- Automatic nested serialization
- Query filtering
- Custom actions
- Full CRUD operations for all models
🌟 Key Concepts¶
ModelSerializer¶
Central to Django Ninja Aio CRUD - defines schemas directly on models:
class User(ModelSerializer):
username = models.CharField(max_length=150)
class ReadSerializer:
fields = ["id", "username"] # Response schema
class CreateSerializer:
fields = ["username"] # Input schema
class UpdateSerializer:
optionals = [("username", str)] # Partial update schema
Serializer (Meta-driven)¶
Use when you have vanilla Django models and want dynamic serialization without changing your model base class.
from ninja_aio.models import serializers
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)])
Attach to an APIViewSet:
APIViewSet¶
Automatically generates complete CRUD endpoints:
@api.viewset(User)
class UserViewSet(APIViewSet):
pass
# Generates: List, Create, Retrieve, Update, Delete
Custom Views¶
Extend with custom endpoints:
from ninja_aio.decorators import api_post
@api.viewset(User)
class UserViewSet(APIViewSet):
@api_post("/{pk}/activate")
async def activate(self, request, pk: int):
user = await User.objects.aget(pk=pk)
user.is_active = True
await user.asave()
return {"message": "User activated"}
📄 License¶
This project is licensed under the MIT License - see the LICENSE file for details.
☕ Support¶
If you find Django Ninja Aio CRUD useful, consider supporting the project:
🔗 Links¶
- Documentation: https://django-ninja-aio.com
- GitHub: https://github.com/caspel26/django-ninja-aio-crud
- PyPI: https://pypi.org/project/django-ninja-aio-crud/
- Django Ninja: https://django-ninja.dev/
- Example repository: https://github.com/caspel26/ninja-aio-blog-example
Built with ❤️ using Django Ninja