Sometimes, you need part of your Django application to run from the command line. These scripts can be caching jobs that run periodically to speed up performance or data collection jobs that pull information from various sources into your application. Although James Bennett has a great article on writing standalone Django scripts, I just wanted to update it with changes that have happened since 2007. I ran into this problem about the same time on both a work project and on a personal project.
The problem with standalone scripts
The basic problem is that you want your script to run in the context of your application. You want to access your databases in the normal way and you want to import modules by the same paths. In general, you can do all of this work yourself by making sure your PYTHONPATH is correct and DJANGO_SETTINGS_MODULE is set properly, but it is so much easier to just create a custom management command. This is especially convenient since it makes your script portable (Windows, Mac & Linux — crontab & schedule tasks) as well easily distributable with your application.
Custom management commands
A custom management command is a command that can be run from manage.py. Essentially, Django requires that you create the following type of structure under your application:
1 2 3 4 5 |
management/ __init__.py commands/ __init__.py mycommand.py |
Then, in mycommand.py you must subclass django.core.management.base.BaseCommand like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
from optparse import make_option from django.core.management.base import BaseCommand, CommandError # Class MUST be named 'Command' class Command(BaseCommand): # Displayed from 'manage.py help mycommand' help = "Your help message" # make_option requires options in optparse format option_list = BaseCommand.option_list + ( make_option('--myoption', action='store', dest='myoption', default='default', help='Option help message'), ) def handle(self, *app_labels, **options): """ app_labels - app labels (eg. myapp in "manage.py reset myapp") options - configurable command line options """ # Return a success message to display to the user on success # or raise a CommandError as a failure condition if options['myoption'] == 'default': return 'Success!' raise CommandError('Only the default is supported') |
This will allow you to run (mycommand is named for mycommand.py):
1 |
> python manage.py mycommand |
or create a crontab entry as follows:
1 |
* * * * * cd /home/path/to/project && python manage.py mycommand |
Conclusion
This provides the cleanest, most flexible way to build and support standalone scripts that can be used outside of the web server. It will run in exactly the same environment as your application and the same modules used in your application can be used here. The only problem I ran into was that custom management commands must be run from the same directory as manage.py.
Edit: I added thorough documentation on management commands to ticket #9170
Update: The 1.2 documentation contains the changes from my patch.
If you override the __init__ method of BaseCommand, you must still call the BaseCommand constructor. When Django attempts to output an error (raised by CommandError) to the screen, it attempts to style it. Errors will be printed in bold red text or something similar based on your terminal settings.
If you see an error like this:
AttributeError: ‘Command’ object has no attribute ‘style’
You need the following in your Command constructor:
There is no need to change the current directory to where “manage.py” is in the crontab. Just give Python the full path to it, followong with the necesary parameters:
* * * * * python /home/path/to/project/manage.py mycommand
I discovered later that this had to do with my hosting provider rather than Django.