Using ActiveRecord Migrations in Django Apps?
In Episode 40 of the Rails Envy podcast, using Rails ActiveRecord migrations in Django apps was mentioned.
I'm comfortable with AR, and AR migrations are more readable and convenient than using plain SQL to migrate the models, so I decided to give it a try. (I use plain SQL when I migrate the database schemas in my Django projects and I haven't used Django Evolution yet.)
Here's what I did. (If you wanna see the AR migration script now, click here.)
Test Bed
I created a Django project, set up my database, and updated settings.py.
$ django-admin.py startproject mysite
$ cd mysite
$ mysqladmin create mysite
$ mate . # open the project in TextMate and update the db connection info in setting.py
Next, I created a Django app called blog. Yeeeehaw! Blog!
$ ./manage.py startapp blog
And added the Post model in blog/models.py
# blog/models.py
from django.db import models
# Create your models here.
class Post(models.Model):
title = models.CharField(max_length=60)
body = models.TextField()
date_created = models.DateTimeField()
date_published = models.DateTimeField()
Then I added the blog application in the INSTALLED_APPS tuple in settings.py, validate, and created the blog tables.
$ ./manage.py validate
$ ./manage.py syncdb
Check out the CREATE TABLE statements for the blog app.
$ ./manage.py sql blog
BEGIN;
CREATE TABLE `blog_post` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`title` varchar(60) NOT NULL,
`body` longtext NOT NULL,
`date_created` datetime NOT NULL,
`date_published` datetime NOT NULL
)
;
COMMIT;
A New Attribute
Uh. Okay. Now, for the fun part. I added date_updated to the Post model. Kept it simple.
class Post(models.Model):
.
.
.
date_updated = models.DateTimeField()
And checked the newer CREATE TABLE statements for the blog app.
$ ./manage.py sqlall blog
BEGIN;
CREATE TABLE `blog_post` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`title` varchar(60) NOT NULL,
`body` longtext NOT NULL,
`date_created` datetime NOT NULL,
`date_published` datetime NOT NULL,
`date_updated` datetime NOT NULL
)
;
COMMIT;
Hmm.. A new NOT NULL column date_updated.
The ActiveRecord Migration Script
Now for the standalone ActiveRecord migration script.
# File: add_date_updated.rb
require 'rubygems'
gem 'activerecord'
require 'activerecord'
# Setup database connection
ActiveRecord::Base.establish_connection(
:adapter => "mysql",
:database => "mysite",
:username => "teem",
:password => "passw0rd",
:host => "localhost"
)
# Migration script
class AddDateUpdated < ActiveRecord::Migration
def self.up
add_column :blog_post, :date_updated, :datetime, :null => false
end
def self.down
remove_column :blog_post, :date_updated
end
end
# Migrate
AddDateUpdated.migrate(:up)
The script is pretty straight-forward. It sets up the database connection, defines the migration script, and finally runs the migration.
You can always migrate down by replacing the :up with down, but that's a little obvious, right?
Also, note that the table name is blog_post, and also the :null => false condition.
Notes, Etc.
-
It's not really DRY, since the db connection information is in the script itself. Maybe creating a database.yml and putting db connection info there could make things DRYer. The Ruby script and the
settings.pycan simply load the yaml instead. - I didn't bother with the schema information, versioning stuff. Should I?
- If you wanna try it out, the ActiveRecord documentation is here and the ActiveRecord Migration documentation here.



