Lincoln Loop depend on open source projects, and we like to give back to the community whenever we can. Some of our team have our own open source projects, and we’re encouraged to set aside some of our working week to support and develop them. One of my projects is Nanodjango, and I recently gave a lightning talk about it at Djangocon US.
Getting started with Django can be daunting - an empty project currently has 13 files across 3 directories. Although this will really help you as your project grows, if it’s your first time, it can be a bit confusing - and even if it’s your hundredth time, it can be a bit of a faff if you just want to do a quick experiment.
One of the reasons people prefer Flask and FastAPI is because they’re easy to get started with - you open up a text editor, write a function, and it just works.
Nanodjango is a project to bring that simplicity to Django, without losing any of its power:
Django in a single file
Django in a single file is nothing new - there have been countless projects and talks, starting in 2009 with djing (Simon Willison) and Django Inside Tornado (Lincoln Loop’s Yann Malet), and more recently Using Django as a Micro-Framework (Carlton Gibson), django-microframework (Will Vincent, and our founder Peter Baumgartner), μDjango (Paolo Melchiorre), django-singlefile (Andrew Godwin) and Django from first principles (Eric Matthes) - and many more in between.
These usually implement Django views and sensibly stop there, but that means you lose Django’s batteries, and there’s no real advantage over using Flask. Nanodjango aims to support all Django’s features, just make them easier to access.
To get started:
$ pip install nanodjango
Let’s implement a version of Flask’s “Hello world”. First import and instantiate Django, then define a function which returns our content, and register it as a view with the @app.route
decorator:
Save it - we’ll call ours hello.py
, but you can call it anything; internally the filename becomes the app’s name, so this is roughly equivalent to manage.py startapp hello
in a normal project.
To run it in development mode using runserver you can use:
$ nanodjango run hello.py
or if you want to deploy it, you can serve it in production mode using gunicorn:
$ nanodjango serve app.py
Nanodjango can also pick up templates, and serve your static files (using whitenoise).
Writing APIs
So that’s Flask - what about FastAPI?
Nanodjango uses django-ninja to provide built-in API support. You can register an API view with @app.api.get
or @app.api.post
, which gives us our version of FastAPI’s hello world:
This uses Django Ninja to give you everything FastAPI has, including nice swagger API docs and schemas using pydantic.
And like FastAPI we can also run asynchronously - just add async
to the view definition,
then nanodjango run
and nanodjango serve
will detect you have async views, and switch to serving using uvicorn
:
Using models
So now we’re at the same level as Flask and FastAPI, but Django has batteries, and we want to use them - so lets create a model:
Here we have a simple model that logs the time that instances are created, and we can use it in our views - everything you can do in a normal project you can do here.
When we run it, it creates a set of migrations for our models, creates a database and applies them, creates us a superuser if we don’t already have one, and then runs runserver
or uvicorn
to give us fully working models:
Django admin
Everything you can do in a normal project you can do here, so we can also use the admin site. You can register it using the normal syntax, but nanodjango gives you an @app.admin
decorator to make things easier:
You can also pass in extra arguments to customise the ModelAdmin:
And now when you run the script, the admin site will register itself at the url /admin/
- customisable, of course.
Share it
At this point nanodjango is a useful tool for experimenting and building prototypes, but where it’s really useful is sharing working examples with other people.
Because it’s in a single file, any nanodjango app can be shared easily, but PEP 723 makes it even simpler - we can specify nanodjango as a dependency at the top, then call app.run()
at the bottom:
and we’ve got a fully-functional self-contained Django site that can be run from a single file, without installing anything other than uv or pipx:
$ uv run hello.py
Convert to a full project
The paradox in fitting a full Django application into a single file is that we lose one of the things that makes Django so good as a production framework - its project structure.
Although a single file is great for a quick experiment or prototype, as our project grows a single file is going to become difficult to maintain.
Nanodjango can help you here too - it has a convert
command which will go away and automatically refactor your single file into a fully structured django project that doesn’t use nanodjango at all - with your models, views, admin, APIs automatically moved to the right places:
$ nanodjango convert hello.py ./hello
And more?
The talk sparked several ideas from members of the community, and several Loopers got together at the sprints at the end of Djangocon to start working on new features. If you’re interested in learning more or contributing to the project, you can find the source over on GitHub at https://github.com/radiac/nanodjango/.