meetteem: tech addict

software development, etc.

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.

  1. 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.py can simply load the yaml instead.
  2. I didn't bother with the schema information, versioning stuff. Should I?
  3. If you wanna try it out, the ActiveRecord documentation is here and the ActiveRecord Migration documentation here.

GottaDash with Shoes

In my previous job, I regularly used the (10+2)*5 procrastination hack to boost my productivity. I was pretty comfortable with the system. It kept me focused in what I was doing. It allowed me to conserve my energy for my tasks. Sticking to the system sort of developed my discipline. Because I was aware that I was working and I had progress in my tasks, I felt encouraged and motivated to even more productive. My boss and colleague suggested that we implement a tool to facilitate that (10+2)*5 hack, and we did. The company named it GottaDash. Mikong made a JavaScript version, while I made a Flex version. In making that Flex version, I had to spend some time learning the basics of ActionScript, MXML, and the SDK because I wasn't familiar with the platform and the languages. My learning of Flex had a very good motivation -- RIA using Flex and Ruby on Rails. GottaDash in Flex was the tool I used at work.

But then my MacBook crashed, and along with its hard drive, all my programs and files were obliterated, including the source code of the Flex version. Sigh. I didn't have the time to re-implement the Flex version, and it already took a lot of my work hours, so I decided not to re-implement it.

Last Saturday, I felt that my body was missing its productivity juices; my mind, its focus. So I quickly cooked up GottaDash using Shoes, a tiny UI toolkit for Ruby.

Coding in Shoes was a little bit challenging. Why? Because I wasn't able to find a comprehensive documentation (in RDoc). I switched back and forth between my code and the Shoes manual. There was one time that I wanted to know the api of the Shoes::Every, because I wanted to subclass it. Nope. I failed to find it. It isn't in the internets? That's weird. I ended up getting the Shoes source code and going through it. I wasn't able to find the documentation for the :styles options for elements.

Another thing I had a problem with was the video command (I only needed to play the alarm sound). You can't play hidden videos. They have to be visible in order to play them. I ended up creating a stack element with a size of 1x1 pixel just to hold the alarm mp3.

So here it is. Grab it, open it with your Shoes installation, and feel the joy of being productive!

P.S. In the spirit of open source, I've uploaded the GottaDash source code in github, if you want to take a look at it. It's released under MIT license.

P.P.S. A friend recommended to me that I should set up a way to accept donations, possibly through PayPal. I haven't done so and I'm still wondering if people would actually want to donate money to me. But I sure hope that this will be useful for many. It's useful for me.

Update There are times when Shoes would crash. I couldn't trace the bug atm, but when I have the time, I'll update the thing, maybe refactor the code a little bit to make it smaller and simpler.

My Reading List

I added a small Django app to sort of track my progress in reading books. I hope this will motivate me to read the books I bought over the past year and a half. Last time I checked, I haven't even touched about 25 of my books.

So little time, so much to do.

Now, to actually add the books to the list...

LOL! Name That Code

LOL. Got this from Mia.

Name That Code

Another Django Update, 1.0 Draws Near

Django 1.0 alpha was released yesterday. I'm running this blog on trunk so I updated this blog to use the bleeding edge feature recently merged to the trunk: the newforms-admin. The last time I updated was when the queryset-refactor branch was merged to the trunk. With Brian Rosner's walkthrough and screencast, the migration wasn't a pain. It took me about 10 minutes to migrate, switching back and forth the NFA wiki and the blog code.

Thank you, Django people. You've made the world a better place.

<< Older Posts