It’s cool that these projects exist but their existence will not dissuade people from using Flask. That seems to be really a fundamental lifestyle and cultural choice.
Hi, I’m the author of nanodjango! The goal of the project is not so much to dissuage people from using Flask and FastAPI; it’‘s more to make it easier to start a Django project. If you look at the hello world tutorials for Flask and FastAPI they’re basically “write your view in a file and it just works”. Django’s is at least 3 commands to create a project, and making changes to at least 2 files in a fairly offputting file structure. It’s a steep learning curve.
Nanodjango solves this by letting you write your code in a single file, and providing convenient commands to run it locally or in production mode. It’s great for experimenting and prototyping, and sharing code (you can uv run nanodjango scripts). Critically, when your project outgrows a single file, you can nanodjango convert it into a full Django project - best of both worlds.
The fact that nanodjango looks a lot like Flask and FastAPI is a credit to those projects - they’ve done a fantastic job of simplifying backend development. Personally I think having access to Django’s batteries makes nanodjango worth a look for anyone thinking about starting something new, but I appreciate it’s not going to be for everyone.
Exactly. And not to put too fine a point on it, there was an impedance mismatch between the ORMs… the SQLAlchemy ORM that everyone uses with flask starts to shine as projects get really large. The Django ORM is great for a quick start. But flask uses the “heavy” ORM and django uses the “quick start” ORM. Getting me the one-file start with the django ORM feels like you’re setting something right with the world.
Thanks for putting this together and publishing it. After noodling around with it a bit, I really like it so far. And I really love that I can convert it into a project that would leave me just where I was if I’d used the default tools. That is spectacular.
That seems to be really a fundamental lifestyle and cultural choice.
I think it’s also (relatedly) an ORM-style choice. If you prefer (or need) the Data Mapper pattern, you’re using flask. If you find Active Record more suitable for what you’re doing, django is the path of least resistance.
Since I usually prefer the Active Record pattern, I find this very appealing… it looks like a nice way to do some quick exploration using an ORM I like, and appears to include a way to convert the single file to a more traditional django project.
I was planning to use django to test out a couple of ideas today, anyway. I think I’m going to use that as an excuse to kick the tires on this.
This is the same sentiment as @pm’s sibling comment but from a totally different angle. Around a decade ago, on someplace like Reddit or HN, I half-remember seeing an argument between Python developers about how to implement a fragment of Scheme. I apologize for the paraphrase as it might be distorted, but I recall one developer saying that the nil list should be implemented like:
class Nil(SchemeObject): pass
nil = Nil()
And the other developer thought it should be:
nil = object()
The first developer thought that Scheme primitives like null? ought to be methods on Nil and thus on SchemeObject:
class SchemeObject:
def isNull(self): return False
class Nil(SchemeObject):
def isNull(self): return True
And the other developer thought it should be direct case analysis:
def isNull(obj): return obj is nil
I think that this may have been around the time of Stop Writing Classes, which definitely captured the post-Flask mood. The point is that the “lifestyle and cultural choice” the second developer, the Stop Writing Classes crowd, and the Flask users made was to write shorter, more direct, less classy code which delegates less behavior to individual objects and treats them more as variables which are subject to case analysis.
Yes, a couple of times. You can import everything and the kitchen sink in views.py.
Still wasn’t nearly as bad as the flask project I got once where somebody had made their a la carte version of django with a bunch of more or less maintained libraries.
While I also have wished for this, every time I’ve attempted to do it myself, I stopped because I found something that suited my need better. 4 of those things that scratched my itch and made me no longer care (in the moment) about having it in a separate package:
In the scenario I have in mind you already have a Django project with models, managers, complex queries, migrations etc and you don’t want to re-write everything.
It would allow you to transition to Flask-based project, for example, without having to re-write the model layer. You could have both apps in production, sharing the same Django models, as you transition to a new Flask based app or maybe continue using the Django app for the plain HTML version but have a second app for the API.
You might like Tortoise, then. The times I’ve tried it, the code for the models (and for interacting with the models) has been very much copy/paste from django. Caveat: it’s been 3 years. Not so much for migrations, but the sane approach there seems to be using django for those until you’re ready to get rid of django completely and move to aerich.
oh, i meant in the context of copilot, cursor, supermaven etc, so llm assisted coding.
having the whole context in a single file makes things a bit easier, for instance: no need to select certain files to send it along with the prompt and it’s easier to apply diffs generated by the llm to a single file.
also if the file fits into the context window (or even if a large related chunk fits), i imagine llm autocomplete works better without indexing the code base and storing embeddings.
so i’d say this single-file approach works well even when llm tooling is limited, maybe only a large context size is needed.
appreciate the reminder to elaborate, my original comment was a bit anemic.
github
It’s cool that these projects exist but their existence will not dissuade people from using Flask. That seems to be really a fundamental lifestyle and cultural choice.
Hi, I’m the author of nanodjango! The goal of the project is not so much to dissuage people from using Flask and FastAPI; it’‘s more to make it easier to start a Django project. If you look at the hello world tutorials for Flask and FastAPI they’re basically “write your view in a file and it just works”. Django’s is at least 3 commands to create a project, and making changes to at least 2 files in a fairly offputting file structure. It’s a steep learning curve.
Nanodjango solves this by letting you write your code in a single file, and providing convenient commands to run it locally or in production mode. It’s great for experimenting and prototyping, and sharing code (you can
uv runnanodjango scripts). Critically, when your project outgrows a single file, you cannanodjango convertit into a full Django project - best of both worlds.The fact that nanodjango looks a lot like Flask and FastAPI is a credit to those projects - they’ve done a fantastic job of simplifying backend development. Personally I think having access to Django’s batteries makes nanodjango worth a look for anyone thinking about starting something new, but I appreciate it’s not going to be for everyone.
This is fun to play with so far.
Exactly. And not to put too fine a point on it, there was an impedance mismatch between the ORMs… the SQLAlchemy ORM that everyone uses with flask starts to shine as projects get really large. The Django ORM is great for a quick start. But flask uses the “heavy” ORM and django uses the “quick start” ORM. Getting me the one-file start with the django ORM feels like you’re setting something right with the world.
Thanks for putting this together and publishing it. After noodling around with it a bit, I really like it so far. And I really love that I can
convertit into a project that would leave me just where I was if I’d used the default tools. That is spectacular.I think it’s also (relatedly) an ORM-style choice. If you prefer (or need) the Data Mapper pattern, you’re using flask. If you find Active Record more suitable for what you’re doing, django is the path of least resistance.
Since I usually prefer the Active Record pattern, I find this very appealing… it looks like a nice way to do some quick exploration using an ORM I like, and appears to include a way to convert the single file to a more traditional django project.
I was planning to use django to test out a couple of ideas today, anyway. I think I’m going to use that as an excuse to kick the tires on this.
This is the same sentiment as @pm’s sibling comment but from a totally different angle. Around a decade ago, on someplace like Reddit or HN, I half-remember seeing an argument between Python developers about how to implement a fragment of Scheme. I apologize for the paraphrase as it might be distorted, but I recall one developer saying that the nil list should be implemented like:
And the other developer thought it should be:
The first developer thought that Scheme primitives like
null?ought to be methods onNiland thus onSchemeObject:And the other developer thought it should be direct case analysis:
I think that this may have been around the time of Stop Writing Classes, which definitely captured the post-Flask mood. The point is that the “lifestyle and cultural choice” the second developer, the Stop Writing Classes crowd, and the Flask users made was to write shorter, more direct, less classy code which delegates less behavior to individual objects and treats them more as variables which are subject to case analysis.
I think the idea behind this is to ease the adoption of Django and not “dissuade people from using Flask”.
I always found setting up a new Django project to be quite the drag.
Copy pasting into a single file with dependencies at the top is actually pretty nifty and would speed things up a lot.
I can see going over 1k LoC with this.
Ever tried inheriting an old, existing one?
Yes, a couple of times. You can import everything and the kitchen sink in
views.py.Still wasn’t nearly as bad as the flask project I got once where somebody had made their a la carte version of django with a bunch of more or less maintained libraries.
Isn’t that every Flask project past a certain size?
100%
[Comment removed by author]
That’s your opinion. Mine is that this is evolving. It was either Django or Flask and projects like this are bridging the gap.
I wish that Django would factor out the ORM into a separate package somehow.
While I also have wished for this, every time I’ve attempted to do it myself, I stopped because I found something that suited my need better. 4 of those things that scratched my itch and made me no longer care (in the moment) about having it in a separate package:
django management commands - write it in a command line tool and call it from an appropriate venv
celery - for firing off long running tasks that use django models
Tortoise ORM - for using django-ORM-like constructs outside of django, especially in async contexts
dj-notebook for using it from jupyter.
Do you have some other scenario where you want the ORM without using django?
In the scenario I have in mind you already have a Django project with models, managers, complex queries, migrations etc and you don’t want to re-write everything.
It would allow you to transition to Flask-based project, for example, without having to re-write the model layer. You could have both apps in production, sharing the same Django models, as you transition to a new Flask based app or maybe continue using the Django app for the plain HTML version but have a second app for the API.
You might like Tortoise, then. The times I’ve tried it, the code for the models (and for interacting with the models) has been very much copy/paste from django. Caveat: it’s been 3 years. Not so much for migrations, but the sane approach there seems to be using django for those until you’re ready to get rid of django completely and move to aerich.
neat approach, also very llm friendly!
What does “llm friendly” mean in this context? An easy way to access LLM APIs? Something LLMs can easily help generate? Something else?
oh, i meant in the context of copilot, cursor, supermaven etc, so llm assisted coding.
having the whole context in a single file makes things a bit easier, for instance: no need to select certain files to send it along with the prompt and it’s easier to apply diffs generated by the llm to a single file.
also if the file fits into the context window (or even if a large related chunk fits), i imagine llm autocomplete works better without indexing the code base and storing embeddings.
so i’d say this single-file approach works well even when llm tooling is limited, maybe only a large context size is needed.
appreciate the reminder to elaborate, my original comment was a bit anemic.