L'uso di questo sito
autorizza anche l'uso dei cookie
necessari al suo funzionamento.
(Altre informazioni)

Tuesday, June 17, 2014

Running Django scripts from the CLI (Command Line Interface)

Like many frameworks embedding an ORM, Django offers an attractive SQL-less interface to your DBMS, whose worth transcends its usefulness in the traditional web interface.

Which one do you like better:

SELECT n.name m.type FROM nodes n, model m WHERE n.mid=m.id AND m.make='Qfwfq';


or

them=Node.objects.filter(model__name='Qfwfq')
for q in them:
 ...

Yes, I thought so.

However, python's inclusion rules combine with Django's deployment rules to make writing general scripts of this sort a little awkward. The problem is that, for the above goodies to work, all of the django instrumentation surrounding your app has to be available to the python interpreter, a job that is usually taken up by manage.py when invoked from the deployment folder.  That is the effect we want to achieve within our own scripts.

After some mulling, this is what i came up with:


#!/usr/bin/python
#File: prologue.py
import os
import sys
#assumes we are in DJANGO_DIR/sbin
#find our own location
_pdir=os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
#find DJANGO_DIR
_cdir=os.path.dirname(_pdir)
#prepend to include path
sys.path.insert(0,_cdir)
#tell django where settings.py is
os.environ['DJANGO_SETTINGS_MODULE']='settings'


I keep all the django related scripts in a sbin/ subdirectory of the directory where manage.py is located and they all start with:

#!/usr/bin/python
import prologue


Obviously, prologue.py must live in the same folder. I then symlink them to somewhere in system's $PATH (that's usually /usr/local/bin).

This way, every script will get the correct sys.path and the right DJANGO_SETTINGS_MODULE from prologue.py; prologue.py is always found because it's in the same path as the script. The abspath(realpath()) incantation does away with finding the actual path of the prologue.py file under symlinks and other surprises.

Some folks may prefer inserting at the end of, rather then in front of, sys.path. They should use sys.path.append(_cdir).

Et voilà.


Edit:
Discussions on reddit on this this topic prompted me to clarify the purpose a bit.

The whole gist of all this is the toolification  of Django. This does not (solely) mean being able to type django commands from the (Linux) command line (though that is nice, too). It  rather means creating scripts that can then be used by other tools, without needing to know where the particular Django app lives, not even within the script code. (Picture wanting to relocate your django app to some other folder.)

This is why the execfile('myfile.py') feature of manage.py is not relevant in this context, nor is the possibility of adding commands to manage.py via the management commands feature of Django: in both instances, one has to run the correct manage.py, which lives in the app dir hierarchy. The django-extensions package, which allows to run jobs from manage.py, succumbs to the same objection here (at least, I suppose: I haven't reaaly looked extensively into django-extensions, though I will as it looks pretty cool in its own right).

No comments: