Using the Django ORM as a standalone component

Django logoI’ve found myself working in Python lately, both for a new project and while preparing a review of the Django book. Working in Ruby I’ve become used to relying on ActiveRecord whenever I need to talk to a database (whether inside Rails or not) and after a little time refamiliarising myself with MySQLdb I realised I was going to need a decent ORM for this project. As well as being road-tested and well documented, I was also looking for something that could either generate its models on the fly, or had a tool to generate them from an existing schema.

I checked out SQLAlchemy and SQLObject, both of which looked like worthy contendors, but couldn’t find the generator I was looking for (if you know of one, please do let me know in the comments!) so I switched over to Django. I couldn’t find much information on using their ORM standalone, so thought I should share what I discovered. Please bear in mind that my python is rather rusty–if there are better ways to do any of this I’d be very pleased to hear about it.

The tool to generate your model classes from an existing database is django-admin.py which should be in your path if you’ve got Django installed. To get that up and running you’re going to need to set a few options.

First up, you’ll need a settings.py file specifying your database details. Mine looks like:

DATABASE_ENGINE = 'mysql'
DATABASE_NAME = 'mydatabase'
DATABASE_USER = 'myusername'
DATABASE_PASSWORD = 'mypassword'

Once that is done and saved, then from the command line you should be able to call:

django-admin.py inspectdb --settings=settings

and the models will be echoed to stdout. To redirect that to a file called models.py you’ll need:

django-admin.py inspectdb --settings=settings > models.py

I found a few areas where the generated objects threw errors. We have a column called ‘try’ which is a python keyword, so that required a small change to the code. And a number of models have foreign key relationships with other models that are declared after them, so a little reorganising was called for. It’d be really nice to have the script handle that, but it wasn’t a big deal to make the changes by hand.

With that done, I tried a very simple new script:

from models import *
a = MyModel.objects.all()
print a

But ran into a few issues:

The ORM is relying on the environment variable DJANGO_SETTINGS_MODULE to tell it where to find your database credentials. You’ll need to set that to point to the settings.py file you created earlier.

You can’t have your models.py file in the same folder as your test script. Doing so throws a “IndexError: list index out of range” error. Instead make a new folder called, say, orm and put models.py in it, along with an empty file called __init__.py which will tell python to treat the folder as a module. You can then update your test script to:

from orm.models import *
a = MyModel.objects.all()
print a

With that done, you should be able to use the ORM just as you would in your Django view code.

Tags: , , , , , ,

10 comments

  1. For SQLObject (not exactly like Django’s, more like ActiveRecord): http://www.sqlobject.org/SQLObject.html#automatic-class-generation

  2. SQLAlchemy has an included ‘SQLSoup’ plugin:
    http://www.sqlalchemy.org/docs/04/plugins.html#plugins_sqlsoup
    Like SQLObject’s fromDatabase, it introspects the database on-the-fly rather than introspecting it once and generating a models specification.

    I have paranoia about code generation in general, so that inspectdb always kind of scared me.

  3. Adam – I tend to prefer introspection too, but for various reasons I wanted to go with some explicit code here. For future projects it definitely sounds like SQLAlchemy is worth some more attention.

    What is it that makes you paranoid about code generation?

    Introspection tends to seem more flexible, particularly when coupled with a separate migrations system, but I’ve found generation to be helpful on a number of occasions.

  4. Nathan Collins

    Thanks for your post. In case someone will find more detail useful, here’s how I adapted your tips to set up my models.py so that it will work both as part of a Django project and standalone. My directory structure is

    db/
    __init__.py
    settings.py
    orm/
    __init__.py
    models.py

    Here’s the code at the top of my models.py

    from os.path import dirname
    import os, sys

    # HACK: Django doesn’t directly support the use of the ORM outside of
    # a Django project. If this module is imported outside of a project
    # some environment needs to be set up. See
    # http://jystewart.net/process/2008/02/using-the-django-orm-as-a-standalone-component/.
    if ‘DJANGO_SETTINGS_MODULE’ not in os.environ:
    sys.path.insert(0, (dirname(__file__) or ‘.’) + ‘/../..’) # put settings.py on path
    os.environ[‘DJANGO_SETTINGS_MODULE’] = ‘db.settings’

    from django.db import models

  5. All I get when I run the django-admin command is:

    ImportError: Could not import settings ‘settings’ (Is it on sys.path? Does it have syntax errors?): No module named settings

  6. os.environ[‘DJANGO_SETTINGS_MODULE’] = ‘db.settings’
    This helped me.
    Thanks a lot

  7. Thanks James!

    If anyone has settings related problems (I did), try using the –pythonpath option to add the path to the settings file to the python path. For example, if you have a directory named ‘appDir’ which contains your settings.py file:

    django-admin.py inspectdb –pythonpath=’/usr/local/appDir/’ –settings=settings

    cheers,
    -tomread

  8. for sqlalchemy, I believe the the argument you are looking for is autload. With either the declarative extension or the mapped table route, you can use that and SA will reflect your table structure