Every Django project starts with a manage.py file in its root. It's a convenience script that allows you to run administrative tasks like Django's included django-admin.

In our last post, we discussed the merits of including a setup.py file in your project to make it installable as a true Python package. We'll build on that and show you how to properly setup a console script in the package.

Hello Entry Points

The setup function provided by setuptools accepts an entry_points keyword argument that is used to dynamically create scripts from a provided Python function on install. If we crack open the existing manage.py and strip it down to its essentials, we can see it isn't really doing much:

import os
import sys

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)

It sets a default environment variable and then calls a function passing in the provided command line arguments.

This can easily be turned into an entry point by simply making it a function inside our project. I typically add it to myproject/__init__.py like so:

import os
import sys

def manage():
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
    from django.core.management import execute_from_command_line
    execute_from_command_line(sys.argv)

Now we adjust our setup.py to add the entry point:

setup(
    ...
    entry_points={'console_scripts': [
        'manage.py = myproject:manage',
    ]}
)

On install, a manage.py script will be created and added to the virtualenv's path.

It's also worth noting that the script's name is purely vestigal at this point. If you are targeting non-Django developers with your project, it might make more sense to rename it to something more logical.

Benefits

There are a few things I like about this approach.

  1. You can run manage.py from anywhere. No need to activate a virtualenv or be in the right directory. Simply run /path/to/virtualenv/bin/manage.py ... and it just works. If your virtualenv is activated, the path will already be set and manage.py will just work without the path.
  2. This is purely cosmetic, but I don't like the proliferation of files in the project root (, .dockerignore, Procfile, webpack.config.js, etc.). One less file makes me happy.

One potential downside to this approach is that manage.py is often a signal to developers, "this is a Django project". By removing it, you're more reliant on the docs to point developers in the right direction. If you prefer to keep your manage.py file, you can still benefit from this approach by wrapping the code inside a function and registering that as an entrypoint.


Have any other Django install tricks you like to use on your projects? I'd love to hear them. Please leave a note in the comments.