meetteem: tech addict

software development, etc.

My Python Plan

I'm a professional Ruby/Rails developer. With Ruby being the tool at work and without any constant Python development work, the skills I've learned are getting blunt. I'm not happy with that. I want to have them back.

Here's a list of my Python related tasks, topics, and things I want to focus on in the coming months:

  • Update my development blog to Django 1.0, use django.contrib.comments this time (January)
  • Move my personal blog from Wordpress to Django (January)
  • Some interesting packages: SQLAlchemy, virtualenv, nose, and zc.buildout
  • TDD in Python
  • Google App Engine
  • PyObjC

I've also subscribed to the Python Magazine just to add motivation.

I certainly hope this will help me get started with Python development again.

View Comments

Using Capistrano to Create A Remote Git Repository

Last night, I thought about buying a GitHub micro plan to host my private projects. The free account in GitHub gives you a 100MB storage and unlimited public repositories, but no private repositories. It has a nice web interface, which makes things like creating repositories easy.

Anyway, after further thinking, I decided not to buy and use my VPS instead. I have a VPS which has several gigs more of storage space, and that's more than enough for Git hosting. And I only want it for my private/personal projects. For open source, GitHub is always there.

One of the things that I seem to keep repeating is setting up a remote git repository. So to address this repetitive task, I created a tiny Capistrano script to do just that: create a remote repository in my VPS.

Here's the capfile.

set :root_path, "/var/git/"
set :box, "server.com"

namespace :git do

  desc "Create a bare git repository"
  task :create, :hosts => box do
    get_repository_name()
    path = "#{root_path}#{gitdirname}"
    create_bare_repository(path)
  end

  desc "View clone urls of existing git repos in #{box}:#{root_path}"
  task :view_clone_urls, :hosts => box do
    sudo "ls #{root_path}" do |ch, stream, data|
      data.split(/\s+/).each do |r|
        puts "#{r.ljust(20)} -> git@#{box}:#{root_path}#{r}"
      end
    end
  end

  def get_repository_name
    set(:gitdirname) { Capistrano::CLI.ui.ask "Enter project name: " }
    set(:gitdirname, "#{gitdirname}.git") if gitdirname[-4..-1] != ".git"
  end

  def create_bare_repository(repo_path)
    sudo "mkdir #{repo_path}"
    run "cd #{repo_path} && #{sudo} git --bare init"
    sudo "chown -R git:git #{repo_path}"

    puts "\n\nClone url: git@#{box}:#{repo_path}\n"
    puts "To add the new remote to your existing git, run: "
    puts "  git remote add origin git@#{box}:#{repo_path}"
    puts "After committing the changes, push the with the following command"
    puts "  git push origin master\n\n"
  end

end

How to use?

Get it and check out the cap tasks.

~/dev/tools$ cap -T
cap git:create          # Create a bare git repository
cap git:view_clone_urls # View clone urls of existing git repos in server.com...
...

Edit the variables root_path and box near the top of the capfile. root_path is the path in the remote machine where the repo will be stored, and box is the server address.

Let's create a remote repository using cap git:create.

~/dev/tools$ cap git:create
  * executing `git:create'
Enter project name: hello-world
  * executing "sudo -p 'sudo password: ' mkdir /var/git/hello-world.git"
    servers: ["server.com"]
    [server.com] executing command
    command finished
  * executing "cd /var/git/hello-world.git && sudo -p 'sudo password: ' git --bare init"
    servers: ["server.com"]
    [server.com] executing command
 ** [out :: server.com] Initialized empty Git repository in /var/git/hello-world.git/
    command finished
  * executing "sudo -p 'sudo password: ' chown -R git:git /var/git/hello-world.git"
    servers: ["server.com"]
    [server.com] executing command
    command finished


    Clone url: git@server.com:/var/git/hello-world.git
    To add the new remote to your existing git, run: 
      git remote add origin git@server.com:/var/git/hello-world.git
    After committing the changes, push the with the following command
      git push origin master

And bind our project to the remote repository using git-remote add and push it to our newly created remote.

  ~/dev/tools/hello-world$ git remote add origin git@server.com:/var/git/hello-world.git
  ~/dev/tools/hello-world$ git push origin master
  Counting objects: 3, done.
  Writing objects: 100% (3/3), 215 bytes, done.
  Total 3 (delta 0), reused 0 (delta 0)
  To git@server.com:/var/git/hello-world.git
   * [new branch]      master -> master

So, there.

View Comments

Django 1.0 Released!

It's finally here!

~$ python -c 'print __import__("django").VERSION'
(1, 0, 'final')

View Comments

The Survey for People Who Make Websites

View Comments

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.

View Comments

<< Older Posts