Using external scripts with django models
I have used a few web frameworks over the years but I think I have finally found the one that suits my particular needs. I have played with RoR, Turbo Gears, Catalyst and a couple of others but none have actually made me want to write code instead of hoping that it allows me to write less. That was until I discovered Django. A friend of mine had said he was using for his website it but for some reason I managed to get it stuck in my head that he was using Mambo CMS so I never really paid it much attention.
Something that I usually need in a web application is some way to interact with it from the command line, I am a systems administrator – CLI is what I do. In the past I have just connected directly to the database from my scripts but it has always bothered my that I have already done all this business logic in my models and I end up having to repeat it in a limited fashion. I have had a brief look around a couple of times but never found anything that works to my satisfaction until I found the following code on this site by James Bennett http://www.b-list.org/weblog/2007/sep/22/standalone-django-scripts/. James has several methods on that page but I think that this one will be best if you plan on distributing your application.
import os from optparse import OptionParser usage = "usage: %prog -s SETTINGS | --settings=SETTINGS" parser = OptionParser(usage) parser.add_option('-s', '--settings', dest='settings', metavar='SETTINGS', help="The Django settings module to use") (options, args) = parser.parse_args() if not options.settings: parser.error("You must specify a settings module") os.environ['DJANGO_SETTINGS_MODULE'] = options.settings LOG_FILENAME = 'logging.log' logging.basicConfig(filename=LOG_FILENAME,level=logging.INFO,)
The issue with this was after a while I realised that it didn’t work unless you were in the base directory of your application, this may be because I was in doing something wrong – I am relatively new to python. Then I thought how does Django do it ? The answer to this lived in the core.management.__init__ method setup_environ(). Anyway I ended up using a mashup of the two concepts to arrive at the following code. It is a bit long winded but it works from any directory so the script can be anywhere. It sets the environment up based on the settings file being in the normal place – in the base directory of the application.
import os import sys from optparse import OptionParser # Parse the command line options usage = "usage: %prog -s SETTINGS | --settings=SETTINGS" parser = OptionParser(usage) parser.add_option('-s', '--settings', dest='settings', metavar='SETTINGS', help="The Django settings module to use") (options, args) = parser.parse_args() if not options.settings: parser.error("You must specify a settings module") # set project_directory, settings_filename = os.path.split(options.settings) if project_directory == os.curdir or not project_directory: project_directory = os.getcwd() project_name = os.path.basename(project_directory) settings_name = os.path.splitext(settings_filename)[0] sys.path.append(os.path.join(project_directory, os.pardir)) project_module = __import__(project_name, {}, {}, ['']) sys.path.pop() os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name, settings_name)
With that added at the top of my scripts I can keep may maintenance scripts in a place more logical like /usr/local/bin instead of the project root and run them like this
/usr/local/bin/myscript.py --settings=/path/to/project/settings.py