meetteem: tech addict

software development, etc.

Viewing posts tagged 'python'

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.

Some Python Notes

I spent the last two days studying (and relearning) advanced Python and Smalltalk.

Here are some of my Python notes.

Disclaimer: I'm not a Python master, though I want to be one.

Classes and Objects

I thought that when you create a class without specifying a parent class, it sets object as the parent class, ala Ruby. I was wrong. Classes that inherit from object are new style classes.

Check this out:

>>> class A: pass
... 
>>> class B(object): pass
...
>>> dir(A)
['__doc__', '__module__']
>>> dir(B)
['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__']

And you can easily add attributes to objects (instances of a class).

>>> class Cat: pass
... 
>>> felix = Cat()
>>> felix.name = 'Felix'
>>> felix.name
'Felix'
>>> def meow(self):
...   if hasattr(self, 'name'):
...     print "%s: meowwww" % self.name
...   else:
...     print "purrrrr"
... 
>>> Cat.meow = meow
>>> dir(felix)
['__doc__', '__module__', 'meow', 'name']
>>> felix.meow()
Felix: meowwww
>>> sylvester = Cat()
>>> sylvester.meow()
purrrrr

I'm thinking of metaprogramming in Python right now. Like, how can I dynamically add a dynamically named method to a class? setattr maybe? And the last time I checked, decorators and descriptors are the most common metaprogramming techniques used in Python.

Descriptors

Here's a guide to Python descriptors.

Descriptors allow you to create attributes of an object that can do some magic when those attributes are read, written, or deleted.

Here's my example:

class EncryptionKey(object):
  def __set__(self, inst, value):
    self.key = value
    if hasattr(inst, 'encrypt'):
      inst.encrypt(value)
    else:
      pass

  def __get__(self, inst, objtype):
    return self.key

class EncryptableString(str):
  key = EncryptionKey()
  def encrypt(self, key):
    self.encrypted = __import__('string').join([chr(ord(c)^key) for c in self], '')

s = EncryptableString("hello")
s.key = 67
print s.encrypted

# output
+&//,

When s.key is set to 67, encrypt is called by the EncryptableString object, which encrypts the current object and the result is saved in the object's encryted attribute. The descriptor protocol allows this callback method to be called. Nice.

Note that for descriptors to work in a class, the class must be a new style class, i.e. a class that inherits from object or type.

Mixin Classes

Got this from here.

You can create mixin classes using multiple inheritance.

class EatingMixin(object):
  def eat(self, food="5 gallons of ice cream"):
    print "I'm now eating %s." % food
    return self


class WalkingMixin(object):
  def walk(self, distance=5):
    print "I'm now walking %d miles."
    return self


class Person(EatingMixin, WalkingMixin):
  def __init__(self, name):
    super(Person, self).__init__(self)
    self.name = name
    print "Hi! I'm %s." % self.name


tim = Person('Tim')
tim.walk()
tim.eat()
tim.eat('lots of vegetables')


# Output:
Hi! I'm Tim
I'm now walking 5 miles.
I'm now eating 5 gallons of ice cream.
I'm now eating lots of vegetables.

Another way is to modify the __bases__ of the class. Using the mixin example above:

class DressupMixin:
  def dressup(self, dress="red evening gown"):
    print "Do I look cute in this %s?" % dress
    return self

class Woman(Person):
  pass

Woman.__bases__ += (DressupMixin,)

Woman('Eve').walk().eat().dressup()


# Output
Hi! I'm Eve.
I'm now walking 5 miles.
I'm now eating 5 gallons of ice cream.
Do I look cute in this red evening gown?

Note that you can modify the __bases__ of the classes on the fly. How's that for power! You can do this:

new_bases = list(Woman.__bases__)
new_bases.remove(DressupMixin)
Woman.__bases__ = tuple(new_bases)

Now, the Woman class and its instances won't have the dressup method anymore. This makes Python classes pluggable.

The only problem I encountered was when I set the __bases__ to () or None. This causes a TypeError to be raised.

More to Come

So, there.

I also played with __metaclass__. And I'll also add my notes on decorators.

24 Hours with Google App Engine

In the past 24 hours, I looked at Google App Engine. The result: the index page of teemlabs.

Yes, it's a static page. But that's not the point. The point is, I'm now more familiar with appengine than I was yesterday, and I'm lovin' it.

Just a run-down of the things I did regarding appengine yesterday.

I downloaded and installed the new Google App Engine Launcher. It's for Apple machines only.

This app is sweet. You want to create new app or deploy your freshly coded app? Or maybe you just wanna view logs? One click is all it will take. Or something like that. :P I also set TextMate as the default editor, and enable the option to open the directory.

I made a widget creation app, whatever you call that thing in iframes or javascript that you embed in your blog or web site. But I chose not to deploy it for personal reasons.

I decided to work on that app using Django. I read this and applied what I read to my code.

I also learned a little about the Datastore API, which allows your app, well, to store data. Django ORM, while I think is really, really nice, isn't supported by Google App Engine. Datastore API is similar to Django models API, so it wasn't much of a hassle. Then, there's the Query class, which has the filter() and order(). Still familiar. And then the GqlQuery class! It's a query interface which uses GQL, an SQL-like query language. Quite familiar but I prefer the less SQL-like Query class to this one.

One thing I failed to try was the validation of attributes of a Datastore object. I think validation is fairly easy to do, as the docs say, the validator is just a Python function, though I have to see this for myself.

I had to look at the Django 0.96 documentation once in a while. My Django's a little bit rusty.

I also downloaded and installed python-dateutil to the app. I had to manually add the directory of the python-dateutil to the Python path before I could import it.

And then teemlabs! I wanted to deploy an app before the day ends. A little bit of CSS here, some Django templates there, I was ready to deploy teemlabs! A single click, Click! Uploading... Failed. Oh! A typo in the app.yaml. app.yaml is used in deploying your app. I mistyped the application to 'teemlab' instead of 'teamlabs'. Tsk.

So there. I don't think I mentioned everything appengine-related I did in the last 24 hours. Oh wait, I also joined the Google App Engine Google Group.

Although some people have blogged about some limitations of appengine, I'm even more excited about appengine, after I've read the docs and all.

Earlier today, Google added two features to appengine: the Images API and the Memcache API. They've updated the SDKs. It was version 1.0.2 yesterday; now, it's version 1.1.0. Sweet.

Good things get even better.