Skip to content

hbakri/django-ninja-crud

Repository files navigation

Django Ninja CRUD

Tests Coverage PyPI version Downloads License MyPy Ruff

Django Ninja CRUD

Django Ninja CRUD is a powerful, declarative, and yet opinionated framework that simplifies the development of CRUD (Create, Read, Update, Delete) endpoints and tests with Django Ninja. It promotes best practices for efficient, robust endpoint creation, allowing you to focus on what matters most: solving real problems. Initially inspired by DRF's ModelViewSet, Django Ninja CRUD evolved to address its limitations, adopting a composition-over-inheritance approach to achieve true modularity – a foundational step towards a broader declarative interface for endpoint creation.

📖 Featured Article

I'm excited to share my recent article: "Introducing Django Ninja CRUD" on Medium. This piece dives into the journey of creating Django Ninja CRUD, detailing its features, benefits, and the paradigm shift it brings to Django development.

✨ Key Features

  • Purely Declarative: Embrace an approach where defining views and tests is a matter of declaring what you want, not how to achieve it.
  • Unmatched Modularity: Tailor your viewsets with the desired CRUD views and customize each view's behavior with ease. Extend the flexibility by creating your own subclasses of the provided views and tests.
  • Powerful Testing Framework: Leverage a matrix-based testing framework for defining diverse test scenarios declaratively.
  • Focus on What Matters: Spend more time solving real-world problems and less on CRUD boilerplate.

Its blend of declarative syntax, modularity, and powerful testing capabilities sets a new standard for developers seeking efficiency and precision.

Django Ninja CRUD is not just a tool; it's a paradigm shift in Django web application development and testing.

📝 Requirements

Python versions Django versions Django Ninja versions

⚒️ Installation

pip install django-ninja-crud

For more information, see the installation guide.

👨‍🎨 Example

Usage

Let's imagine you're building a system for a university and you have a model called Department. Each department in your university has a unique title.

# examples/models.py
from django.db import models

class Department(models.Model):
    title = models.CharField(max_length=255, unique=True)

To interact with this data, we need a way to convert it between Python objects and a format that's easy to read and write (like JSON). In Django Ninja, we do this with Schema:

# examples/schemas.py
from ninja import Schema

class DepartmentIn(Schema):
    title: str

class DepartmentOut(Schema):
    id: int
    title: str

The DepartmentIn schema defines what data we need when creating or updating a department. The DepartmentOut schema defines what data we'll provide when retrieving a department.

Now, here comes the power of Django Ninja CRUD. With it, you can set up the CRUD operations for the Department model with just a few lines of code:

# examples/views/department_views.py
from django.http import HttpRequest
from ninja import Router
from ninja_crud import views, viewsets

from examples.models import Department
from examples.schemas import DepartmentIn, DepartmentOut

router = Router()


class DepartmentViewSet(viewsets.ModelViewSet):
    model = Department
    default_input_schema = DepartmentIn
    default_output_schema = DepartmentOut

    list_departments = views.ListModelView()
    create_department = views.CreateModelView()
    retrieve_department = views.RetrieveModelView()
    update_department = views.UpdateModelView()
    delete_department = views.DeleteModelView()


# The register_routes method must be called to register the routes
DepartmentViewSet.register_routes(router)


# Beyond the CRUD operations managed by the viewset,
# the router can be used in the standard Django Ninja way
@router.get("/statistics/", response=dict)
def get_department_statistics(request: HttpRequest):
    return {"total": Department.objects.count()}

Testing

A key advantage of this package is that it makes your views easy to test. Once you've set up your CRUD operations, you can write tests to ensure they're working as expected. Here's an example of how you might test the Department operations:

# examples/tests/test_department_views.py
from ninja_crud import testing

from examples.models import Department
from examples.views.department_views import DepartmentViewSet


class TestDepartmentViewSet(testing.viewsets.ModelViewSetTestCase):
    model_viewset_class = DepartmentViewSet
    base_path = "api/departments"

    @classmethod
    def setUpTestData(cls):
        cls.department_1 = Department.objects.create(title="department-1")
        cls.department_2 = Department.objects.create(title="department-2")

    @property
    def path_parameters(self):
        return testing.components.PathParameters(
            ok={"id": self.department_1.id},
            not_found={"id": 9999}
        )

    @property
    def payloads(self):
        return testing.components.Payloads(
            ok={"title": "department-3"},
            bad_request={},
            conflict={"title": self.department_2.title},
        )

    test_list_departments = testing.views.ListModelViewTest()
    test_create_department = testing.views.CreateModelViewTest(payloads)
    test_retrieve_department = testing.views.RetrieveModelViewTest(path_parameters)
    test_update_department = testing.views.UpdateModelViewTest(path_parameters, payloads)
    test_delete_department = testing.views.DeleteModelViewTest(path_parameters)

    # You can then add additional tests as needed
    def test_get_department_statistics(self):
        response = self.client.get(f"{self.base_path}/statistics/")
        self.assertEqual(response.status_code, 200)
        ... # Additional assertions

📚 Documentation

For more information, see the documentation.

🫶 Support

First and foremost, a heartfelt thank you for taking an interest in this project. If it has been helpful to you or you believe in its potential, kindly consider giving it a star on GitHub. Such recognition not only fuels my drive to maintain and improve this work but also makes it more visible to new potential users and contributors.

GitHub Repo stars

If you've benefited from this project or appreciate the dedication behind it, consider showing further support. Whether it's the price of a coffee, a word of encouragement, or a sponsorship, every gesture adds fuel to the open-source fire, making it shine even brighter. ✨

Sponsor Buy me a coffee

Your kindness and support make a world of difference. Thank you! 🙏