Skip to content

🥷 Django Ninja Aio CRUD

Test Quality Gate Status codecov PyPI - Version PyPI - License Ruff

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

  • 🚀 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 orjson for 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:

Python
# 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: UserSchemaOut})
async def list_users(request):
    return [user async for user in User.objects.all()]


@api.post("users/", response{201: UserSchemaOut})
async def create_user(request, data: UserSchemaIn):
    user_pk = (await User.objects.acreate(**data.model_dump())).pk
    return 201, await User.objects.aget(pk=pk)

# ... more views for retrieve, update, delete
Python
# 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
class UserViewSet(APIViewSet):
    model = User
    api = api

UserViewSet().add_views_to_route()
# Done! List, Create, Retrieve, Update, Delete endpoints ready

📚 Documentation

Explore detailed documentation for each component:

Models

Views

Advanced Topics

💡 Example: Complete Blog API

Here's a real-world example with relationships:

Python
# 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")


class AuthorViewSet(APIViewSet):
    model = Author
    api = api


class CategoryViewSet(APIViewSet):
    model = Category
    api = api


class ArticleViewSet(APIViewSet):
    model = Article
    api = api
    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


class TagViewSet(APIViewSet):
    model = Tag
    api = api


# Register all views
AuthorViewSet().add_views_to_route()
CategoryViewSet().add_views_to_route()
ArticleViewSet().add_views_to_route()
TagViewSet().add_views_to_route()

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:

Python
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

APIViewSet

Automatically generates complete CRUD endpoints:

Python
class UserViewSet(APIViewSet):
    model = User
    api = api
    # Generates: List, Create, Retrieve, Update, Delete

Custom Views

Extend with custom endpoints:

Python
class UserViewSet(APIViewSet):
    model = User
    api = api

    def views(self):
        @self.router.post("/{pk}/activate/")
        async def activate(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:

Buy Me A Coffee


Built with ❤️ using Django Ninja