50 comments

[ 2.8 ms ] story [ 73.9 ms ] thread
Claude Code is also very good at building basic CRUD apps with Django.
No kidding, it is really good especially with htmx which helps you get some of the advantages of a full SPA without the complexity of a separate frontend.

Been building a project in the side to help my studies and it usually implement new complete apps from one prompt, working on the first try

That's a huge bonus point for Django. It's so prevalent that Claude/Codex are very good at setting it up the right way, using tried and true patterns.

I've been vibe coding some side projects with Claude Code + Django + htmx/tailwind, and when it's time to go some manual work in the codebase I know exactly where things are and what they do, there's way fewer weird patterns or hack the way Claude tends to do when it's not as guided

Thanks for this! I wish there were more cross-comparisons like this out there of what it is actually like to use some of these frameworks, the note on Django being a little less magic than Rails makes me genuinely interested in it.
if you want "less magic than rails" check out ecto, i would say it has less magic than django
I always return to Django for any project. It's fantastic. Enough batteries are included with it that it is very powerful.
Django is objectively the most productive "boring technology" I've ever worked with for developing web applications. They don't regularly add too many bells and whistles on every release, but they keep it stable and reasonably backwards compatible.
I do agree.

Though i must admit that for maybe more complex applications, I feel like aspnetcore also fulfills this definition. I feel like it’s easier to create something more complex with aspnetcore while still keeping the code boring and opinionated.

I feel like Django, for bigger apps, fall apart in the "opinionated" side. For "simple" websites, you can’t go wrong, but for anything really big, basically everyone invents their own project structure.

But don’t get me wrong, I still love Django for what it is and it’s my first love in web frameworks anyway.

And I’d go further and say that the Django documentation is so awesome, that 15 years ago, it was where I learnt how websites/http/etc… really worked.

Django aside, I think this is a really important point:

  Being able to abandon a project for months or years and then come back
  to it is really important to me (that’s how all my projects work!) ...
It's perhaps especially true for a hobbyist situation, but even in a bigger environment, there is a cost to keeping people on hand who understand how XYZ works, getting new people up to speed, etc.

I, too, have found found that my interactions with past versions of myself across decades has been a nice way to learn good habits that also benefit me professionally.

This is the main reason I'm extremely disciplined about making sure all of my personal projects have automated tests (configure to run in CI) and decent documentation.

It makes it so much easier to pick them up again in the future when enough time has passed that I've forgotten almost everything about them.

Do you preemptively force yourself to allocate time for documentation maintenance ?
> (configure to run in CI)

Every time I've believed this, I came back to broken CI.

Depending on your language ecosystem, your CI workflows and build deps stand a greater chance to break than your code.

This is also why I write a formal requirements document for all but the smallest throw-away projects. Much easier to know wtf you were thinking 18 months ago if you write down wtf you were thinking at the time.
After spending a lot of my time on Django, it's fine for simple to moderately complex things. The ORM mostly good. DRF is fine for APIs. And the admin is super nice as well.

But once something gets significantly complex, the ORM starts to fall down, and DRF becomes more of a hindrance.

But if you're just doing simple CRUD apps, Django is perfectly serviceable.

What does significantly complex mean though? You have to make sure you understand the queries made by the ORM, avoid pitfalls like SELECT N+1 queries and so on. If you don't do this, it'll be slow but it's not the ORM's fault - it's that of the programmer.
If you're doing simple CRUD apps, try https://iommi.rocks/ which we built because imo it's way way too slow and produces too much code to use standard Django to make CRUD stuff.
In my experience, django-ninja is a great alternative to DRF, precisely because it's so thin, unlike DRF.
> But once something gets significantly complex, <insert name of literally any tech you're incompetent in> starts to fall down.

Vague arguments like this are categorically useless.

> I love being able to backup by just doing a VACUUM INTO and then copying the resulting single file.

Naively, I would probably just copy the sqlite file. Is that a bad idea?

That's fine if SQLite isn't running, but you risk corruption if you copy a file while it is being actively written to.

VACUUM INTO eliminates that risk.

The Django ORM / migrations are still basically unmatched in happiness factor.
100%

I am quite surprised that most languages do not have an ORM and migrations as powerful as Django. I get that it's Python's dynamic Meta programming that makes it such as clean API - but I am still surprised that there isn't much that comes close.

I much prefer Python but am not really seeing any point to doing anything other than JavaScript for web projects at this point.

I also do not see much reason to do more than emit JSON on the server side.

The author makes a great last point about Settings and it’s something I’ve not considered… ever! I wonder if there’s a feature request for this because having a pre-configured object would be nice for the ability to verify correctness on startup.
In hindsight, maybe I should've tried to use Django for my previous project instead of build a lot of custom stuff in Go and React. It was basically an admin interface, but with dozens of models and hundreds if not thousands of individual fields, each with their own validation / constraints. But it was for internal users, so visually it mainly needed to be clear.
I've lobbied to replace our internal tool with a django admin panel. I prototyped it and it showed that it would reduce our code by > 15k lines.

Any internal webapps I need to build like this will 100% be set up with django in the future due to this. I don't need it to be pretty, I just want the UI, database migrations, users, roles, groups, etc for free

It's so easy to add bits to the admin as you need them.

I wish building the frontend of a Django app was as easy as the admin (though Wagtail can get close and HTMX).

After working with Django for 8 years, I find it hard to move on to anything else. It's just the right amount of magic, and just the right amount of flexibility, and it's just such a joy to work with.

Re: Django is OK for simple CRUD, but falls apart on anything complex - this is just untrue. I have worked in a company with a $500M valuation that is backed by a Django monolith. Reporting, recommender systems, file ingestion pipelines, automatic file tagging with LLM agents -- everything lives inside Django apps and interconnects beautifully. Just because it's a Django app doesn't mean you cannot use other libraries and do other stuff besides basic HTTP request processing.

Recently I had the misfortune of doing a contract on a classic SPA project with Flask and sqlalchemy on the backend and React on the frontend, and the amount of code necessary to add a couple of fields to a form is boggling.

How do the apps "interconnect"? In my experience, unless you're careful, a Django monolith quickly becomes a big ball of mud. I've recently started to use Tach to try to combat this.
Some more Django recommendations from Frank Wiles of Revsys (Django consultancy): https://frankwiles.com/questions/starting-django-projects/

I'll add a few of my own:

* Set up the project using uv

* I name the django project "project"; so settings are project/settings.py, main urls are project/urls.py, etc

* I always define a custom Django user model even if I don't need anything extra yet; easier to expand later

* settings.py actually conflates project config (Django apps, middleware, etc) and instance/environment config (Database access, storages, email, auth...); I hardcode the project config (since that doesn't change between environemnts) and use python-dotenv to pull settings from environment / .env; I document all such configurable vars in .env.example, and the defaults are sane for local/dev setup (such as DEBUG=true, SQLIte database, ALLOWED_HOSTS=*, and a randomly-generated SECRET_KEY); oh and I use dj-database-url to use DATABASE_URL (defaults to sqlite:///sqlite.db)

* I immediately set up up ruff, ty, pytest, pre-commit hook and GH workflow to run ruff/ty/pytest

Previously I had elaborate scaffolding/skeleton templates, or nowadays a small shell script and I tell Claude to adapt settings.py as per above instructions :)

> * settings.py actually conflates project config (Django apps, middleware, etc) and instance/environment config (Database access, storages, email, auth...); I hardcode the project config (since that doesn't change between environemnts) and use python-dotenv to pull settings from environment / .env; I document all such configurable vars in .env.example, and the defaults are sane for local/dev setup (such as DEBUG=true, SQLIte database, ALLOWED_HOSTS=*, and a randomly-generated SECRET_KEY); oh and I use dj-database-url to use DATABASE_URL (defaults to sqlite:///sqlite.db)

There is a convention to create "foo_settings.py" for different environments next to "settings.py" and start it with "from .settings import *"

You'll still want something else for secrets, but this works well for everything else, including sane defaults with overrides (like DEBUG=False in the base and True in only the appropriate ones).

I've been really enjoying ruff/ty on my non-Django projects. Was there anything special you had to do to make ty play nice with Django? I kind of assumed with how dynamic a lot of its functionality is ty would just throw a type error for every Model.objects.whatever call.
> use python-dotenv

If you're using UV why would you not use uv.env instead?

Nice.

As a mostly-django-dev for the last 15 years, who's been exposed to FastAPI and various ORMs again recently, I should get round to write a doc about some Django bits.

Django is pretty nice, the changes between versions are small and can be managed by a human.

Part of the reason that you can have the big ecosystem is that there is a central place to register settings and INSTALLED_APPS, middleware etc.

That enables addons to bring their own templates and migrations.

There is a central place a bit further up in manage.py and that enables you to bring commandline extras to Django (and many of the things you install will have them).

Coming to a FastAPI app with alembic and finding a lot of that is build-it-yourself (and easily break it) is a bit of a shock.

The Django ORM at first can seem a little alien "why isn't this sqlalchemy" was my reaction a long time ago, but the API is actually pretty pragmatic and allows easy extension.

You can build up some pretty complex queries, and keep them optimised using the Django-Debug-Toolbar and its query viewer.

The ORM, Templates and other parts of Django pre-date many newer standards which is why they have their own versions. As a Django dev I only just discovered the rest of the world has invented testcontainers, and databases as a solution for a problem Django solved years ago with it's test database support.

I quite like the traditional setup where you have settings/common.py and then settings that extend that - e.g local.puy production.py

If you ever need a CMS in your Django project I strongly recommend Wagtail, it came after the initially most popular django-cms and learned a lot of lessons - feeling much more like a part of Django.

It has the same feeling of being productive as Django does when you first use it.

> Coming to a FastAPI app with alembic and finding a lot of that is build-it-yourself (and easily break it) is a bit of a shock.

I briefly played with FastAPI, and after the same shock I discovered Django-Ninja [1]. It's modeled after FastAPI and async-capable (if you are inclined, but warning, there be dragons). It plays nicely with all parts of Django, including the ORM.

[1] https://django-ninja.dev/

Yes, the previous time I tried to use FastAPI I moved the project to Django-Ninja and development accelerated massively.

Django has so many things that just work - I take storing the session in the database for granted and there were only half finished solutions for this.

Sorting out migrations was a pain outside of Django, even in the new project I'm in with more experienced devs on alembic a lot of things had to manually built and I regularly have to manually edit migrations.

On Django I only have to manually edit the migrations when I'm building a data migration and I can take for granted they just work.

> as a solution for a problem Django solved years ago with it's test database support.

I believe it's now accurate to even say "decades ago".

>If you ever need a CMS in your Django project I strongly recommend Wagtail, it came after the initially most popular django-cms and learned a lot of lessons - feeling much more like a part of Django.

Nope. I would choose plain Django 100% of the time, especially with LLMs. Wagtail is an antipattern.

Wagtail is great for what it's good at.

If you've ever had to use Django-CMS you'd understand.

I went from a Wagtail project to a Django-CMS project (at a company that was upstreaming bits to Django-CMS) and there were so many things where it used the database badly (the usual antipattern of a loop over some queries).

It's easy to structure queries in django in an optimised way as long as you architect around .filter and not .get.

I don't think I ever said anything about Django-CMS? It may be far worse than Wagtail. Using neither is much better.
IMO Django is a buggy and poorly designed framework that locks you into bad decisions.

It's a combination of things that all suck: the - ORM (sqlalchemy is better in every possible way. django's orm is very poor and can't express a lot of sql constructs at all) - templates (jinja2 is basically identical except performant and debuggable) - routing (lots of wsgi routers exist and are lightyears ahead of django)

Don't use Django.

Reference (me saying the same thing 16 years ago): https://news.ycombinator.com/item?id=1490415

Django is great, but it lacks typing in code, but especially in docs. It would spare me a lot of bugs.

IMO, type annotations should only be omitted in obvious cases or simple/MVP projects.

Cool stuff. I just started open sourcing a command-line tool for deploying Django to a server. It handles SSL certs, databases and backups, automatic error emails, and background tasks via celery / redis. The best part? It does not need Docker. It just runs everything on bare metal.

1: https://github.com/mherrmann/djevops

I've had the misfortune of working in FastAPI a number of times, and, as somebody who has been using Django since 2008, Django developers have no idea how spoiled they are to have such a tremendous framework.

Django does not provide magic, but it's ORM is like pure sorcery compared to the garbage out there (SQLModel, etc.).

Django provides the necessary tools to build terrific software. However, it does not prescribe extra extraordinarily high-level architectural guidance, so it leaves that up to you. For example, if you are building an app that will have a main UI for users to login, a backend UI for managers to view reports, an API for consumption by outside developers, Django doesn't provide some kind of automatic recommended pattern for structuring your app to support all this. However, it provides the exact ingredients you need to do this on your own. In the example a gave, you would simply structure your directories to reflect the various UI / inputs modalities, and then point a path to each section (urls.py). If you want to host your API as a separate service, you just define a WSGI/ASGI app and pointed to a separate urls.py, then start that container using that WSGI/ASGI app, simple as that. Django does not have any kind of magical mono effect structure to it. It is just a collection of unbelievably useful and clean and simple API that you can use to compose software at least 5x more quickly then you can with e.g., FastAPI. I'm in the second team I've been in that is using FastAPI, and it is uncomfortably slow, you wouldn't even believe it. With FastAPI or other API/micro frameworks, I think the perception is that "it's just python" as if Django is something else, and so people start off app.py and they "see now I can start from this fresh canvas", and then they learn after spinning their wheels for two years that having a solid ORM is actually important, having migrations that are always correct and automatically generated is important, having an admin interface for viewing, creating, managing local/testing data while debugging things, etc. is important, having an ORM that lets you do aggregation and annotation so easily that you find yourself wanting to make reports just as an excuse to use these features, is important, having a template system that lets you generate dynamic pages without having to set up a full SPA for every little interface that you want to make allows you to make a quick prototype or even a permanent UI of some kind without having to involve a team of front end developers who will then begin to dictate your entire backend architecture through their litany of ever changing API endpoint requirements. Speaking of, the common pattern that I have noticed with SPA/API based develop is that the front end wants to be large and in charge, until it comes time to validate data inputs or have to do anything with data that requires looking at it holistically (for example, given a list of orders, if there are any orders that have items that are backordered, provide a warning at the top of the page… SPA developers completely crushed by this requirement, now the backend has to add additional information via the end point called "are there backorder items", etc.). So you end up with this hodgepodge of mess, instead of creating a prototype of your interface and then coming back and deciding which things need to be "reactive", and either shoehorning those things in or rewriting your UI because it's absolutely necessary.

This has become quite the rant, but as somebody who has worked on software for nearly 20 years and can hand write HTML, CSS, JavaScript, use Svelte / React, Python, Django, SQL, etc., I've learned a LOT and seen a lot and I can tell you absolute certainty that choosing Django for a new project is the absolute most effective choice you can make on your path to success.

Pardon my huge voice-to-text comment ^