Command usage
The nanodjango
shell command has several subcommands.
Run using nanodjango
Nanodjango provides two commands to run your script, which will make and apply migrations, and ensure you have a superuser.
Development mode
To run your script locally, use development mode:
nanodjango run script.py [host:port]
This uses runserver
(or uvicorn
for async views), and uses Django’s static
to serve static and media files.
Production mode
If you are deploying your script to a server, run it in production mode:
nanodjango serve script.py [host:port]
This uses gunicorn
(or uvicorn
for async views), serving static files using
whitenoise
.
Static files will be collected into settings.STATIC_ROOT
which is
static-collected
by default. Note: this directory will be wiped and recreated each
time you run serve
.
This mode does not serve media files; you should put this behind a web server such as
nginx
, and use that to serve your media files directly.
Root files
In both development and production, nanodjango will also look for a PUBLIC_DIR
(public
by default), and if it exists will serve any files at the root (using
WHITENOISE_ROOT
) - useful for favicon.ico
, robots.txt
etc.
Note: PUBLIC_DIR
will be ignored if WHITENOISE_ROOT
is set.
Running your script directly
You don’t need to use the nanodjango
command - you can call app.run()
from the
bottom of your script, eg:
from nanodjango import Django
app = Django()
...
if __name__ == "__main__":
app.run()
You can then run the script directly to launch the Django development server:
python hello.py
Running it as a standalone script
You can take it a step further and add a PEP 723
comment to the top to specify nanodjango
as a dependency:
# /// script
# dependencies = ["nanodjango"]
# ///
from nanodjango import Django
app = Django()
...
if __name__ == "__main__":
app.run()
This will allow you to pass it to uv run
or pipx run
, to run your development
server without installing anything first:
# Create a temporary venv with ``nanodjango`` installed, then run the script
uv run ./script.py
# Same, but using pipx:
pipx run ./script.py
Run using WSGI or ASGI
If you prefer to run gunicorn
or uvicorn
directly, you can pass nanodjango’s
app = Django()
to a WSGI server:
gunicorn -w 4 counter:app
or if you have async views, you can use an ASGI server:
uvicorn counter:app
Because the WSGI and ASGI handlers are different, the nanodjango app
will offer WSGI
by default, and automatically swap to ASGI if an async
view or API endpoint is
found. If you want to override this behaviour, you can specify the handler:
gunicorn counter:app.wsgi
uvicorn counter:app.asgi
Management commands
The nanodjango
command provides a convenient way to run Django management
commands on your app:
nanodjango manage <script.py> [<command>]
If the management command is left out, it will default to runserver 0:8000
- these
two commands are equivalent:
nanodjango manage counter.py
nanodjango manage counter.py runserver 0:8000
You can perform any management command:
nanodjango manage counter.py migrate
For commands which need to know the name of the app, such as makemigrations
,
nanodjango uses the filename as the app name - eg:
nanodjango manage counter.py makemigrations counter