Primary image for Running your Django project with __main__.py

Running your Django project with __main__.py

I don’t like Django’s manage.py. My gripes against it are:

  1. The plethora of files that clutter the root directory of our repos annoys me. manage.py is just one more in a long line of those.
  2. In my opinion, there are more “Pythonic” ways to execute code.

In a previous post, I talked about how you can move the code into your project and use packaging tools to create a manage.py on your PATH during installation. In this post, we’ll look at another approach using __main__.py

What is __main__.py (and __main__)?

The Python docs do a good job of explaining this topic, so I’ll give you the tl;dr here. __main__.py provides a command line interface to a Python package. It can be executed with python -m mypackage. Here are some common ones you may have seen in the wild:

  • python -m pip
  • python -m venv
  • python -m json.tool

You may also be surprised to learn that python -m django can be used in place of the django-admin command. 🤯

The “magic” here is that each one of those packages has a __main__.py which defines what it should do when run from the command line.

In each of these, you’ll see a section like this at the bottom of the file:

if __name__ == "__main__":
    do_something()

__name__ is only set to __main__ when it is the entrypoint for execution. If another Python module imports it, __name__ will not equal __main__. We can use this to define the function we want to call when the file is executed from the command line.

Moving manage.py

When you run django-admin startproject myproject, you’ll get a directory structure like this:

myproject/
├─ manage.py
├─ myproject/
│  ├─ __init__.py
│  ├─ ...

If we move manage.py to myproject/__main__.py, we can replace our calls to manage.py with python -m myproject ....

Is it a good idea?

Despite my personal preference, I’d be hesitant to do this on anything beyond a personal project. While it might not be a Python convention, manage.py is a Django convention. Following conventions leads to less confusion when onboarding new developers. The existence of the file signals (to Django developers, at least) that this is a Django project. They’ll know what that file means and how to use it. Another benefit is that it’s tab-completable in your shell. Typing python -m myproject ... for every command is less than ergonomic 😅.

That being said, you might find this technique handy when you publish a library that will get installed in your Python path. In that case, manage.py won’t get distributed without jumping through some extra packaging hoops. Another nice place for entrypoints is when using shiv to turn your project into a single-file zipapp. Check out my talk from DjangoCon 2019 if you’re more interested in that.

Peter Baumgartner

About the author

Peter Baumgartner

Peter is the founder of Lincoln Loop, having built it up from a small freelance operation in 2007 to what it is today. He is constantly learning and is well-versed in many technical disciplines including devops, …