Suggestions added to Django manage.py
My recently merged PR for ticket #28398 adds very simple ‘did you mean’
suggestions to Django’s
manage.py command, which is the primary way of interacting with Django from the terminal. So in Django
2.1 this is what you will expect to see if you misspell a management command:
$ python3 manage.py run-server
Unknown command 'run-server'. Did you mean runserver?
Type 'manage.py help' for usage.
to get correct is ‘developer friendliness’, e.g tools that lend themselves to being easy to use out of
the box with little or no configuration. This means that tools are expected to auto-reload, produce coloured output
and be fairly simple to invoke right off the bat. Obviously this isn’t a universal rule - things like webpack configuration
is an art unto itself, but in general I think it holds true, and I think perhaps the Python ecosystem could take a few lessons
from NodeJS (especially around packaging, but that’s getting better thanks to
pipsi). That being said, since
switching to gatsby I’ve found a few
bugs with the development experience.
This may seem like something pretty insignificant to anyone who has worked with Django for any extended period of time but for an absolute beginner it can be quite helpful. I’ve onboarded a couple of junior developers who had very little experience with Django and one thing that tripped them up initially was the name of the commands they should run, and how if they made a typo in the command name the output is indistinguishable from the command not existing at all.
The implementation for this was very simple, and you can see the code here.
Specifically it leverages a great standard library function
difflib.get_close_matches, which simply
takes a search string (the command a user is trying to execute) and an iterable of things to match from. So when a user
invokes a command that isn’t known we feed a list of all possible commands (built in and third party!) into this method,
along with the users input, and it returns the closest match.
I think this method is a hidden gem in the standard library, and can be invoked like this:
from difflib import get_close_matches
matches = get_close_matches('run-server', ['runserver', 'migrate'])
# matches = ['runserver']
If it wasn’t for this standard library method Django would have to ship it’s own algorithm for suggesting commands. There is a somewhat simple and effective algorithm you can use, called the Levenshtein distance, but having a standard library function to take care of this for us is great as it takes the maintenance burden for that potentially hairy code away from Django.