# django-ninja-crud: declarative CRUD viewsets for Django Ninja

> django-ninja-crud is a MIT licensed library that gives Django Ninja a set of composable view classes, so a list, create, read, update and delete endpoint set is declared as class attributes rather than written out as handlers. It suits teams with several similar resources and a repetitive API layer, and it is a 0.x library whose published releases stopped in 2024.

**hbakri/django-ninja-crud** — 🧩 Modular, composable API views for scalable Django Ninja projects, with built-in CRUD.

- Repository: https://github.com/hbakri/django-ninja-crud
- Website: https://django-ninja-crud.readme.io
- Stars: 645 · Forks: 25
- Language: Python
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/hbakri-django-ninja-crud

## The repetition problem the library was written for

The project describes itself as introducing modularity to API development with Django Ninja, providing reusable, composable API views ranging from built-in CRUD operations to complex custom endpoints, supporting both sync and async implementations, all with minimal boilerplate.

The origin story is given in a note in the README, attributing the library to hours of wrestling with repetitive, complex and hard-to-maintain APIs at a DjangoCON Europe 2024 talk. That is the design brief: an API layer where the same five handlers exist for every model, each one reimplementing the same pagination, lookup and error handling.

The approach is to move those handlers from functions into declarative class attributes. Instead of writing a list function, a create function, a retrieve function, an update function and a delete function for a Department resource, you declare five attributes on a class. The library supplies the implementations.

The stated adoption path is gradual. The library claims to enhance an existing Django Ninja project without changing its structure, so you can add declarative views where they help and leave plain Django Ninja handlers where they are better. That is the right shape for a library that intends to live alongside hand-written code in the same project, and it is the reason the composability features matter more than the CRUD helpers.

## Views, viewsets and what the composition buys you

Four ideas carry the design: modular views, flexible built-in CRUD views, viewset composition and Django Ninja integration.

The modular views piece extends APIView so that you can state intent and reuse components for repeated business logic. The signatures are unrestricted, which is what allows the same view to be written as a sync function or an async one. Given that Django 4.2 and 5.x both support async views, and Django Ninja supports them too, a library that forces one or the other would be annoying; this one accepts both.

The built-in CRUD views are ListView, CreateView, ReadView, UpdateView and DeleteView. The documentation is explicit that they are blueprints: use as-is, customize, or use them as a starting point for your own implementations. They support any path parameters, pagination, filtering and decorators.

The composition piece is the one that goes beyond CRUD. An APIViewSet groups related views that share attributes, and, according to the documentation, a set can hold multiple instances of the same view type. The stated use cases are API versioning and alternative representations of the same resource, which are exactly the cases where hand-written duplication gets ugly. Two representations of a product, one JSON and one something else, at the same path shape, is a real problem that a plain viewset of functions solves badly.

Django Ninja integration is the fourth pillar, and it means the objects you get back are Ninja routers and views rather than a parallel framework. The docs also describe a set of reusable views in the examples directory, under examples/reusable_views.py.

## A department API declared in class attributes

The worked example in the README is a university department API, and it is short enough to read in full. The model is a single field, the schemas are a request schema and a response schema, and the views are class attributes on an APIViewSet.

The viewset is the part to look at:

```python
class DepartmentViewSet(viewsets.APIViewSet):
    api = api
    model = Department

    # Define all CRUD operations with minimal code
    list_departments = views.ListView(response_body=List[DepartmentOut])
    create_department = views.CreateView(request_body=DepartmentIn, response_body=DepartmentOut)
    read_department = views.ReadView(response_body=DepartmentOut)
    update_department = views.UpdateView(request_body=DepartmentIn, response_body=DepartmentOut)
    delete_department = views.DeleteView()
```

Three things are worth noticing. The api attribute binds the set to a NinjaAPI instance, and the model attribute gives the library the queryset and the model to work from. Each view class takes the request body and the response body as type arguments, so the schema is declared at the same place as the endpoint, and DeleteView takes neither, because a delete has no body.

The attribute names are yours to choose. Here they are list_departments, create_department and so on, and those names become the operation names, which is how a group of related operations ends up in one generated router. Naming them descriptively rather than list, create and read is what keeps a set of them from colliding when several sets are combined.

Custom endpoints do not have to go through the CRUD machinery, and the example shows that directly, with a plain Django Ninja route registered on the same api object for a statistics endpoint that counts the model.

## The generated routes and the defaults shortcut

The README lists the routes the example produces: GET / for a paginated list, POST / for creation, GET /{id} for retrieval, PUT /{id} for update, DELETE /{id} for deletion, and the custom GET /stats/ endpoint. The list endpoint comes with pagination limit and offset, and the documentation notes the configured exclude patterns still apply when specific files are given.

There is a shorter form for straightforward cases. Instead of passing request_body and response_body to every view, the APIViewSet class can supply default request and response bodies, and the views then inherit them. The README shows a variant of the department viewset where the body types are declared once on the class rather than on each view.

That is where the value of the library is clearest. A resource whose input and output schemas are the same shape for every operation costs one declaration rather than five, and a resource with different shapes, say a create body that differs from a read body, overrides only the views that need it.

The trade is that the shorthand removes a place to put per-operation configuration. Anything specific to one endpoint, an unusual status code, a different lookup field, an extra permission, has to be expressed by subclassing that view rather than by passing a parameter, so the mental model for extending is subclass and override, not configure. Teams who find that natural will like the library; teams who prefer explicit per-endpoint configuration will find the blueprint more useful than the shorthand.

## Version ranges, packaging and the release gap

The requirements are stated as badges, which are the most compact form of the constraint set: Python versions, Django 4.2, 5.0 and 5.1, and Django Ninja 1.0, 1.1, 1.2 and 1.3. The package metadata agrees and adds detail. requires-python is >=3.9, the dependencies are django>=4.2 and django-ninja>=1.0, and the classifiers list Python 3.9 through 3.13, the same Django versions, and the AsyncIO and Pydantic framework tags.

The build is Hatchling, with the wheel packaging the src/ninja_crud directory, so the import name is ninja_crud. The library is MIT licensed, and the development status classifier says Development Status :: 4 - Beta. That is the honest label and worth taking at face value: a 0.x package with a beta classifier is a package whose API may still move.

The release history is the part to weigh before depending on it. The tagged releases are v0.6.2 from 2024-09-22, v0.6.1 from 2024-08-04 and v0.6.0 from 2024-07-28, and the project metadata declares version 0.6.2. The repository is not archived and the last push was on 2026-10-19, so the code is still being worked on, but the release line has not moved since 2024. For a library you would install into a production Django project, that is a reasonable thing to weigh against the alternative of writing the same five handlers by hand.

The repository itself is well equipped for the maintenance it has had: a tests directory, an examples directory with a tests subdirectory, a pre-commit configuration, a CONTRIBUTING file, and a dev dependency set including coverage, pre-commit and pydoc-markdown for generating API documentation.

## Where django-ninja-crud is the wrong tool

The first case is a project with one resource. Writing five handlers by hand for a single model is faster than learning a library and its extension points, and the library's own value proposition is about repetition across many resources.

The second case is a project where every endpoint is genuinely different. If your update endpoint returns a different shape, your list endpoint needs custom filtering, and your create endpoint has a multi-step workflow, then the CRUD views are blueprints you will all override, and the abstraction is costing you rather than saving you.

The third is a project with unusual conventions. The generated routes are conventional, GET /, POST /, and the id-based paths, and the library is built around the Django ORM model and a NinjaAPI instance. If your API is organised around actions rather than resources, or if you need non-model sources for a list, the library does not reach it.

The alternative worth comparing is not another library, it is the plain Django Ninja route. The whole mechanism is a declarative wrapper over functions that take a request and return a response. Writing those functions yourself gives you the same routing, the same Pydantic schemas and the same generated OpenAPI documentation, with no abstraction between you and the framework, and it is a few lines per endpoint. The difference in approach is that django-ninja-crud buys you the shared behaviour, the pagination and the filters that you would otherwise write and maintain once per resource. For three resources, the library is overhead. For thirty, it is probably the cheapest option available, and the fact that it is MIT licensed and small enough to read means the decision is reversible either way.

## Conclusion

django-ninja-crud earns its place in a Django Ninja project that has several resources sharing the same five endpoints, where the repetition is the actual source of bugs. It is the wrong choice for a project with one or two hand-written endpoints, for a team that wants full control over the response shape of every route, and for anything that needs a maintained release cadence, since the latest tagged release is v0.6.2 from 2024-09-22 even though the repository is not archived. Before adopting it, confirm your Django and Django Ninja versions fall inside the supported range, decide whether the abstract views or your own subclasses will own the shared logic, and keep the custom endpoint path open so you are not forced to route everything through the CRUD classes.

## FAQ

### How do I install django-ninja-crud?

Run pip install django-ninja-crud. The package requires Python 3.9 or newer, Django 4.2, 5.0 or 5.1, and Django Ninja 1.0 through 1.3, and it is MIT licensed.

### What CRUD views does django-ninja-crud provide?

ListView, CreateView, ReadView, UpdateView and DeleteView. They can be used as-is, customized, or used as blueprints for your own implementations, and they support path parameters, pagination, filtering and decorators.

### How do I add a custom endpoint alongside the CRUD views?

Register a normal Django Ninja route on the same NinjaAPI instance the viewset uses, as the department example does with a get_department_stats function for a /stats/ path, and it appears alongside the generated CRUD routes.

### Can I use django-ninja-crud with async views?

Yes. The library states that views are defined by stating intent with unrestricted function signatures supporting both sync and async implementations, and the package classifiers list the AsyncIO framework tag.

### How recently has django-ninja-crud been released?

The latest tagged release is v0.6.2 from 2024-09-22, preceded by v0.6.1 in August 2024 and v0.6.0 in July 2024. The repository is not archived and the last push was on 2026-10-19.

## Sources

- [hbakri/django-ninja-crud on GitHub](https://github.com/hbakri/django-ninja-crud)
- [License: MIT](https://github.com/hbakri/django-ninja-crud/blob/main/LICENSE)
- [Project website](https://django-ninja-crud.readme.io)
- [README](https://github.com/hbakri/django-ninja-crud/blob/main/README.md)
- [Releases](https://github.com/hbakri/django-ninja-crud/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/hbakri-django-ninja-crud
