Using the Django ORM as a standalone component
I’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=settingsand 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.
Comments are closed.
about 2 years ago
For SQLObject (not exactly like Django’s, more like ActiveRecord): http://www.sqlobject.org/SQLObject.html#automatic-class-generation
about 2 years ago
Ah, great. Thanks Ian.
about 2 years ago
For SQLAlchemy + Elixir. Seems to be active.
http://code.google.com/p/sqlautocode/
about 2 years ago
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.
about 2 years ago
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.
about 1 year ago
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
about 1 year ago
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
about 1 year ago
os.environ['DJANGO_SETTINGS_MODULE'] = ‘db.settings’
This helped me.
Thanks a lot
about 1 year ago
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