Hacker News new | past | comments | ask | show | jobs | submit login
Show HN: Django Ninja CRUD – Rethinking CRUD Operations in Django (github.com/hbakri)
100 points by hichambakri on Dec 13, 2023 | hide | past | favorite | 46 comments
I've developed Django Ninja CRUD, a tool to simplify CRUD operations in Django by leveraging Django Ninja's strengths. It aims to make CRUD operations more intuitive, modular, and efficient, especially for large-scale projects.

Key aspects: - Declarative, intuitive CRUD operations. - Modular approach with composition-over-inheritance. - Enhanced testing capabilities for robust applications.

The tool is designed to transform Django development workflows, improving efficiency and maintainability.

Read more about the development, features and roadmap here: https://medium.com/@hbakri/introducing-django-ninja-crud-ee9...

GitHub Repository: https://github.com/hbakri/django-ninja-crud

I'm eager to hear your feedback and hope it adds value to your Django projects!




I've been making APIs in django for years and for each project, at some point the API behaviour needs to differ from the model. It is not clear from the README how would one handle that use case.


That's the one reason I'm credited as an early contributor to Django-REST-Framework: suggesting to the author early on that he split DB model from API model


One thing I’ve never gotten about DRF’s model of using a Serializer is that it very quickly becomes overloaded with responsibilities: serializing and deserializing, validating request and response data, even updating an ORM model directly and bypassing the ViewSet entirely.

In my experience this leads to business logic getting littered all over a code base, and sometimes being inconsistent, mostly because a strong service layer doesn’t exist like in other frameworks I’ve used.

It is likely that I am just thinking about it incorrectly, or porting over my own expectations from other frameworks onto Django/DRF. Is there a better way to think about it?


those I think are optional conveniences, you can use less "smart" abstractions and do those things more explicitly. if I recall. I try not to use python/django anymore, too painful.


Are you referring specifically to separating the Serializer from the Model?

Do you have links to that convo? I love to read how design choices like that are made, and obviously that is one of the defining (correct) choices for DRF.


it was a DM on Convore in 2011 so it is long gone. my name is also only credited in an old CREDITS file in the DRF git history (not sour grapes, just explaining where these credits are)

I shared a prototype REST framework I'd built that separated Serializer from Model as inputs to views, yes, under different names, and he liked the idea and took it from there

this is the prototype repo: https://github.com/aehlke/django-catnap you can see the RestResource, RestModelResource classes. this was distinct from tastypie's approach (popular at the time)


Neat story! I design physical consumer goods products, and love seeing something I've worked on pop up into my "real life". Not being much of a programmer, do you think there's an equivalent feeling for coding? And was this one of those instances? I'll be honest, I'm getting 50/50 mildly entertained/mildly bitter vibes off your comment but only replying out of curiosity. Like I said, the intuitive understanding of "code" as a craft is somewhat foreign to me.


oh I'm glad someone else took the idea and made it work. I'm very glad to avoid the work that it took to build Django-REST-Framework :) I only prototyped my work originally because I couldn't find such a solution, not because I actually wanted to work on one.

I just couldn't tolerate tastypie, and I was sick of people learning from Rails to design thin wrappers over their data models for APIs (because of how easy Rails made that), I wanted people to move on from coupling data layer to API representations in such a 1:1 way as the default.


Excited to look, thanks for sharing!


No affiliation to the project, but isn't it? It shows 'input' and 'output' schemata explicitly declared, which happen to match the model in the simple example, but could presumably be different (otherwise what's the point of them).


Yes, very common thing is to have some feel read-only but still appear in the response to POST/PATCH.

Also the set of fields changing depending on the access level of the user (admin-only fields).


Agreed it's not super clear. But if you look where the ViewSet is defined, there is a regular function based route at the bottom. I'm pretty sure the idea is if your create method needs a different input schema for example, you just remove the `create_view = views.CreateModelView()` line and drop back to a regular function based Ninja view. This is similar to DRF how you can customize which methods your viewsets act on by listing out Mixins, but much more elegant (IMO).


I use Django ninja because it’s minimal and uncoupled from everything else. Like the framework it is based on fast api.

Coupling to the ORM the api contract (schemas) is much closer to Django rest framework than id like.

My approach is to have a schema factory that takes query results and outputs instances schema models. The schema handles all the contract level validation.

What benefits do you reason your project brings over my approach? I’m genuinely interested in a technical discussion. Not starting a flame war or baiting you.


This is awesome to see - always support new options tackling big fundamental systems.

I haven't looked at Ninja CRUD in detail, but I want to tell you about the pain points of DRF that we have and hopefully that can help you in you journey.

Currently we use DRF and DRF-Spectacular. I LOVE drf-spectacular - once you learn the DSL and get past all it quirks (there are a lot), it's extremely powerful and keeps API docs up to date with little to no effort.

DRF I could easily leave it behind. It's quite annoying to do things that aren't the core way.

If it's not a ModelSerializer and not a model based field, it gets janky fast.

- data validation is a terrible experience when you have a Model.full_clean(). This rightly could be Django's fault, but Form, Model, Serializer validations are all separated and cannot be unified. I just want a Data Validation backed by code (instead of sql).

- non-model fields has a lot of boiler plate for something so simple

- to_repesentation and from_representation.

But DRF and DRF-spectacular combination is so easy to manage a public API it's crazy, so I stay for now.


I've created APIs in multiple languages and the patterns are typically the same:

* Define your DB schema - OK, no way to avoid this

* Repeat yourself with ser/deser schemas - tedious

* Repeat yourself with basic CRUD endpoints - further tedium

* Eventually lose the will to live

It's so frustrating when there's so much plumbing involved before you even get to the business logic.

I've previously used Django but had discounted it for MVPs due to its relatively sluggish performance and lack of typing, but benchmarks from pypy and typing have made me change my mind. Spring Boot is also a front-runner, but the lack of admin interface and user auth system (I don't want to run Keycloak but would integrate Firebase auth) have tipped me to Django.

When I used DRF previously it followed the above pattern and was slow and tedious to develop. Django ninja looks great (auto generating ser/deser schemas from models, lightweight annotations). Removing the boilerplate to serve a CRUD API also looks super helpful, so thanks, I'll give it a try.


Wow, what a whirlwind couple of days! I'm thrilled and humbled by the response to Django Ninja CRUD on HN. Seeing it climb to the top 30 and witnessing the community's engagement, not to mention the spike in GitHub stars (now over 250!), has been beyond my expectations... Your support and interest mean the world to me!

My apologies for not responding sooner to your lively discussions. Balancing my role at Picsellia with the unexpected but wonderful excitement here meant I couldn't engage as quickly as I would have liked. But I'm here now, eager to dive into your comments and feedback. Thank you all for making this such an incredible experience. Let's get the conversation going!


I am working on a project due to a recent lay off, and holiday seasons the worst to land interviews it feels like, was using DRF, but might check out Django Ninja to compare.

The only thing I didn't like about DRF was the documentation felt like it needed a little more massaging when I used it some years back. I guess you could say this of any project really.


Can you explain from a high level why I’d want to use Django Ninja over DRF?

There’s a vibe, or assumption around Ninja that it’s “newer”, or “like FastAPI”, or “the way forward”, none of which are objective benefits per se.

I wouldn’t even say I’m an advanced DRF user, but with a handful of small functions to create ModelSerializer subclasses and ModelViewSet subclasses, I have basically auto-CRUD for any Django model in DRF, and Ninja always seemed verbose by comparison.

Maybe I just have really simple use cases that work with this approach. Like, are there examples you can share that would be painful in DRF that are easier to navigate in Ninja? Or is it just personal preference of liking the more freeform style of FastAPI?


DRF falls apart very quickly when you get away from the "simple CRUD" use cases.

The Serializer mechanism causes N+1 query behavior by default with non-trivial models. This is exacerbated by the fact that they all run in sequence because there's no async support.

It's documentation is also pretty poor, IMO.

> There’s a vibe, or assumption around Ninja that it’s “newer”, or “like FastAPI”, or “the way forward”, none of which are objective benefits per se.

The creator of DRF also created starlette, which underpins FastAPI. If DRF works for you cool, but that doesn't mean the community hasn't moved on.


This is my expo with DRF exactly. If you want to serve ORM models over REST, it’s amazing and fast. If you have complex business logic it becomes a nightmare.


It's a much nicer development experience because of typing and pydantic. If you are using a language server, it's a game changer.

Ninja also has async view support, I'm not sure the status of DRF in this regard.

The auto generated docs are much nicer (swagger).

It's MUCH more lightweight than DRF. Unless you are using OP's package, there are no giant class based view hierarchies and viewsets and all that, just plain functions for views. In which you define the input and output as pydantic schemas, it's so simple and refreshing.


I think the big issue is that everyone like pydantic and are willing to put it in places it may not really see a big benefit, over say, DRF.

OTOH, if you're a big fan of NoSQL databases, then pydantic is a godsend.


It will never stop being funny to me when NoSQL people need schemas for their schemaless database.


It's almost as if schemas and static types were a good idea/expression of a fundamental reality.


They have a schema, they just don’t want to write it down. So they write it down in code instead of course. Which isn’t fragile at all…!


As mentioned elsewhere in this thread, simple CRUD usually stops being simple CRUD pretty quickly.

If you are actually doing simple CRUD, then DRF's ModelViewSets are great, and Pydantic would indeed be an overkill. However once the view diverges from your DB model, writing Pydantic models IMO is FAR superior than having to write custom DRF serializers.


The key there is 'IMO' isn't it?

If the DB is your schema, great, DRF is awesome. But if the DB is not your schema, no problem, DRF still can handle it.

And with DRF you get CRUD by default. And if you don't want CRUD, no problem, DRF has generic API views too.


So exactly like this package and ninja? Except instead of writing un typed drf serializers you get to write small, concise pydantic models.


DRF is very dynamic. While useful, it does too much magic for my taste. Pydantic pure data models, combined with typeguard annotations do it for me. Plain functions with defined input output types. It helps validate all the assumptions and catch errors earlier.


Ninja supports async, DRF is still synch only?


I think you should move a lot of what you wrote in your Medium post into the Readme, as high level documentation.

It will have the secondary benefit of not being paywalled by Medium.


why do CRUD endpoints even exist? I can't think of a scenario in which I receive something from the front-end, do NOTHING, and just pass it all to DB, even sanitized.


From a first glance, it's a love child of FastAPI and Django?


Quick question about django ninja it is just fastapi integrated to work with the django admin and Orm right ?


If the API is tightly coupled to the data, just use the ORM, Luke. Django, DRF, Django-Filters, DRF-Spectacular. DRF has gotten more things right than wrong.

What DRF and Django get right is that the DB is the utimate schema. Why be afraid of that?


DRF is also great because it allows flexibility when you dont want the API to mirror the DB, which is a lot of the time.


DB is not always the schema. An abstraction layer is essential


And DRF still gives you that.


I'll be blunt, having experienced the damage caused by DRF Model* accelerators in mid sized codebases I just hate it.

CRUD stops being CRUD quickly, these CRUD accelerators just intruduce more non linearity into the development. Its easy to hit a wall and hack around the accelerator to make a little bit custom behaviour leaving behind API implementations that are totaly "irregular" from each other each one mixing different low level changes in the middle of high level accelerators.


Thanks for raising an important point, @pdhborges. You've highlighted the limitations often encountered with traditional CRUD accelerators, especially as projects scale. Django Ninja CRUD is designed to tackle exactly these challenges. Its compositional approach offers flexibility to adapt and customize as needed, without the mess of hacking around the accelerator. It's all about making it easier for developers to maintain consistency across APIs while allowing for the unique customisations each project requires.

And to echo @WD-42's point, if a specific Django Ninja CRUD view doesn't meet your evolving needs, you can seamlessly switch it out for a custom view written in vanilla Django Ninja. It's designed to be flexible and developer-friendly, ensuring you're not locked into a one-size-fits-all solution.


I'm always sensitive to this issue, because I've seen it happen too. Starlite (now Litestar) has fairly good escape hatches I think: you can convert a database model to an API model with a method call, but you can also modify and add fields, or create a totally separate representation, or return multiple database models from one API call. So far so good.


I think that's why the author went with a composable approach, as opposed to say, DRF's ModelViewSets. Similar, but I think the escape hatch here is much easier to open. I like it.


I think Rails made a good decision by adding code generators and calling it 'scaffolding'. The rails CLI will give you all the CRUD you want. Of course, a lot of libraries try to abstract that with DSLs or extra boilerplate.

Doing it at runtime is a lot more difficult and complicated; why not just use parameterised templates to create the right files in the right place?


IMO, all of these acronyms are crap except for HTTP. Stop trying to write these one size fits all APIs and just provide an HTTP server that takes arbitrary data and responds with arbitrary data. If your app has functions that work this way, your API can work that way, too.


Well you could certainly have a laugh being overheard by non-engineer colleagues while talking about 'ninja crud'!


Absolutely haha! It can be quite a challenge to sound professional and serious while explaining 'Ninja CRUD' to anyone, engineers and non-engineers alike. Always leads to some interesting conversations!




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: