How to package a nanodjango app¶
You may want to add a nanodjango site to a bigger project, or you may want to turn a nanodjango app into a standalone project on PyPI (such as privipod, a nanodjango-based secret sharing tool).
To do this, you may want to put your nanodjango script inside a package - for example:
where app.py is your nanodjango script, and you want to launch it from
__main__.py - but using the name of your package, myproject.
Nanodjango was always designed and intended to be run as a single file, where it picks up the app name from the file name. However, needs must, so there is limited support for what you’re trying to do.
However, be warned: here be dragons.
Running with a different name¶
When you instantiate Django, pass it the APP_NAME of your containing package:
app = Django(APP_NAME="myproject")
This is going to fix your paths and module lookups so that your nanodjango script will
think it’s actually called “myproject”, and it’ll look for its static files, templates,
migrations etc in your myproject/ dir - ie myproject/static/ etc.
Potential issues¶
However, I warned you there are dragons:
This approach has not been widely used, and is likely break in exciting ways if you push it too far.
This also means it’ll expect your
myproject/templates/etc are intended for your nanodjango app;… which means anything under
myproject/static/andmyproject/public/will be public to anyone who can access the webserver;… and it’ll overwrite anything in
myproject/static-collected/if you’re running in production mode;… and it’ll create any migrations it needs to in
myproject/migrations/.It will be treating your
myproject/dir as your project root - so your default sqlite database will also end up in your Python installation’ssite-packages, which means it’s not exactly safe.
So, non-exhaustive list of things you will want to do:
Specify a different database path
Always ship with complete migrations (or never ship migrations) - you could end up with clashes if your users’ migration history doesn’t match yours
Be careful to avoid directory name clashes - perhaps specify custom paths for static, public, templates, static-collected, migrations etc.
How to actually get it to run¶
Your __main__.py could look something like this:
from .app import app
if __name__ == "__main__":
app.run()
But chance are you’re going to want to control the Django settings before you
instantiate it. The easiest pattern for this is probably to set some global variables
somewhere before you import your app; for example, you could have this setup:
You can see this is a convoluted and messy pattern, but it works. There are other approaches, but this is probably the simplest way to crowbar an existing script into a package.