Python Tip: Conditional Expressions

I learned something new today about Python 2.5 and newer. I thought it was so nifty that I decided to write a blog article about it. Hopefully someone out there finds it as interestingly useful as I do.

One of the things I find myself doing quite often in my code (be it Python, PHP, Java, or what have you) is a simple conditional assignment like so:

if foo:
    bar = 'baz'
else:
    bar = 'qux'

In a lot of languages these days, you can use a ternary operator as follows to turn these four lines into a one-liner:

<?php
$bar = $foo ? 'baz' : 'qux';
?>

However, this ternary operator does not exist in Python. I have used various means in the past to accomplish the same task without using code like we saw in the first code block above, but they were all very hackish. Today I was happy to learn that there is an official way to do it in Python:

bar = ('baz' if foo else 'qux')

I'm not sure which one is more confusing to newbies: Python's conditional expressions or other languages' ternary operator. Personally, I prefer constructs like these to make my code more concise. I my mind, they also make the code more readable. I have heard some folks argue that ternary operators and the like obfuscate the code more than necessary, so they discourage the use of such tactics and recommend using the classic approach featured in the first example.

For those who are interested, I learned about it and a few other neat things in Python 2.5 and newer at http://www.python.org/doc/2.5/whatsnew/pep-308.html.

Giving OpenSUSE 11.1 An Honest Chance

I've decided that if I ever want to really understand Linux, I'll have to give as many distributions as possible a chance. In the past, I've tried to use OpenSUSE on my HP Pavilion dv8000 laptop, but it never seemed quite as robust or useful as many other distributions that I've tried on the same machine.

With the recent release of OpenSUSE 11.1, I downloaded the final 32-bit DVD ISO as I normally do for newly released distributions (even if I don't plan on using them--it's an addiction). I proceeded to install the GNOME version of it in a virtual machine to see what all the hubbub was about. Evaluating an operating system within a virtual machine is not the most effective way to do things, but everything seemed fairly solid. As such, and since I have always had difficulties keeping any RPM-based distro around for any length of time, I plan on using OpenSUSE 11.1 through March 2008 (perhaps longer if it grows on me). If it hoses my system, I will go back to something better. If it works, I will learn to use and appreciate it better.

The Installation

The first step when the installation program starts is to choose what language to use, after which you choose the type of installation you're going to be doing. Your choices are:

  • New Installation
  • Update
  • Repair Installed System

You also have the option of installing "Add-On Products" from another media. At this step, I chose to do a new installation.

Next, you get to choose your time zone. The interface is very intuitive. You get a map of the world, and you click on the region you want to zoom in on. Once you're zoomed in, you can select a city that is near you to specify your time zone. Alternatively, you can choose your region and time zone from a couple of drop down lists.

After setting your time zone, you get to choose which desktop environment you want to install. Your choices are:

  • GNOME 2.24.1
  • KDE 4.1.3
  • KDE 3.5.10
  • XFCE 4.4
  • Minimal X Window
  • Minimal Server Selection (Text Mode)

I will choose to install GNOME because it seems to be the desktop of the future, especially with the hideous beast that KDE has become in the 4.x series...

Now you get to play with the partitioning. Usually the installer's first guess is pretty good, but I've got a different arrangement for my partitions, so I'm going to customize things a bit.

The next step is to create a regular, unprivileged user account for your day-to-day computing needs. This screen is pretty self-explanatory if you've ever registered for an e-mail address or installed any other operating system.

One thing that seems to have been added to OpenSUSE 11.1 is the option to use your regular user password as the root password. This is probably a nice addition for a lot of people, but I'd rather feel like my computer is a little more secure by having a different password for administrative tasks.

You're also give a few other options, such as being able to receive system mail, logging in automatically, and modifying your authentication settings. Other than the administrative password option, I left everything the same. If you're like me, and choose to have a different administrative password, you will be prompted to enter the new password at the next step.

Finally, you're shown a summary of the installation tasks that will take place. I'm going to customize my software selection just a bit so I don't have to do it manually after the installation is complete. For example, while I do like GNOME to a degree, I prefer to use KDE 3.5.x, so I will choose to install that environment as well just in case I need the comfort of KDE programs. Also, since I like to use the command line interface for a lot of things, I will choose to install the "Console Tools" package, just because it sounds useful. Lastly, I will choose to install a few development packages, such as C/C++, Java, Python, and Tcl/Tk. These changes bumped up my installation size from about 2.8GB to just over 4GB.

After reviewing the remaining tasks, all you need to do is hit the "Install" button. You will be prompted to verify your desire to install OpenSUSE, after which the package installation will begin. While the installation is taking place, you have the option of watching a brain-washing slideshow, viewing the installation details as it progresses, or reading the release notes.

The actual installation took nearly 40 minutes on my laptop. While this isn't necessarily a great improvement over past releases, I'm sure the story would have been much different had I not customized the software I wanted to have installed. The introduction of installation images a few releases ago drastically improved installation times. If you don't customize your package selection, you'll probably notice the speed difference.

When all of the packages have been installed, the installation program begins to configure your newly installed OpenSUSE for your computer, with a "reboot" in between. This is when all of your hardware, such as your network adapters, graphics adapter, sound card, printers, etc are probed and configured. Strangely enough, this step seems to take a lot longer than it does in Windows, which is usually not the case with Linux. What is OpenSUSE up to I wonder?

When all is said and done, the installation program finishes on its own and loads up your desktop.

Annoyances

There are a couple things that really annoyed me right off the bat about OpenSUSE 11.1. The first was that the loading screen and installation program didn't use my laptop's native resolution. My screen is capable of 1680x1050. The installation program chopped off about 1.25 inches of screen real estate on either side of the program. I don't know if this was an intentional occurrence or not. It seems like the artwork in the installation may have been limited to a non-widescreen resolution. If so, that's completely retarded. I'd like to think that more computer users these days have a widescreen monitor than not, at least the ones who would be playing with Linux.

The second annoyance was that the installation program wouldn't use my external USB DVD drive, which I like to think more reliable than my internal DVD drive. I mean, everything would start up fine--I got the boot menu, the installation program loaded fine, and things seemed like they would work. That's up until the package repositories (the DVD) were being built. Then the USB drive just kept spinning and spinning. Once I popped the disc into my internal drive the program proceeded as expected.

Your Desktop

I thought it was interesting that I chose to install GNOME, but since I chose to install KDE 3.5.10 alongside it that's what it booted me into after the installation was completed. No real complaints, though, since I prefer KDE anyway. Nonetheless, I switched back to GNOME to stretch my limits all the more. At least the desktop took up the full resolution that my screen can handle, unlike the installation program and boot screen.

Things seem fairly responsive... nothing like Slackware though. I just received a little popup notification with an excuse for the lag I might be experiencing: the daily indexing has commenced and should be finished soon. Whatever it's up to, it's taking up a consistent 100% of my CPU. How nice. I hope whatever it's indexing ends up being useful.

Sound worked right from the get-go, which is nice. Hardware acceleration for my Radeon Xpress 200M doesn't work, nor does my Broadcom wireless card. These will be fixed soon.

The Wireless

It looks like the most important step in getting my wireless to work was executing these commands as root:

/usr/sbin/install_bcm43xx_firmware
modprobe b43

I did a lot of stuff to try to get my wireless to work before I executed those commands, but nothing did the trick until I tried them. Also, to make the wireless available each time you reboot without requiring the modprobe b43 command, you need to edit your sysconfig.

To do that, open up YaST and find the "/etc/sysconfig Editor" option. Expand the "System" node, and navigate to Kernel > MODULES_LOADED_ON_BOOT. Then put b43 in the value box. Apply the changes. The next time you reboot your computer, the wireless should be available from the get-go.

The Video Card

This section only really applies to folks with ATI graphics adapters.

I found a tutorial on ubuntuforums.org, strangely enough, which described the process for getting ATI drivers to work on OpenSUSE 11.1. The first step is to download the official ATI drivers for Linux. Each of these commands should be executed as root:

wget https://a248.e.akamai.net/f/674/9206/0/www2.ati.com/drivers/\
linux/ati-driver-installer-8-12-x86.x86_64.run

Next, you need to download the kernel source and ensure that you have a few other utilities required for compiling a kernel module:

zypper in kernel-source gcc make patch

Now you should be able to run through the ATI driver installation utility, accepting all of the defaults:

sh ati-driver-installer-8-12-x86.x86_64.run

If you're on 64-bit OpenSUSE, you need to take an extra step to make the driver available:

rm /usr/lib/dri/fglrx_dri.so && ln -s /usr/lib64/dri/fglrx_dri.so \
/usr/lib/dri/fglrx_dri.so

Backup your existing xorg.conf configuration file and configure Xorg to use the new driver:

cp /etc/X11/xorg.conf /etc/X11/xorg.conf.orig
aticonfig --initial -f

Finally, configure Sax2 with the ATI driver:

sax2 -r -m 0=fglrx

Upon rebooting your computer, you should be able to use the hardware-accelerated 3D capabilities of your ATI card. To verify that things are up and running, execute fglrxinfo as a normal user. This command renders the following output on my system:

display: :0.0  screen: 0
OpenGL vendor string: ATI Technologies Inc.
OpenGL renderer string: ATI Radeon Xpress Series
OpenGL version string: 2.1.8304 Release

Other Thoughts

After having played with OpenSUSE 11.1 for a couple hours, I think I might be able to keep it around for a little while. Despite the lack of speed exhibited by other Linux distributions, the "stability" that OpenSUSE seems to offer is attractive to me. It will likely take some time to get used to RPMs over DEBs for package management.

How bad can it be? I mean, it comes with OpenOffice 3.0.0, which is nice. It can handle dual-head mode on my laptop thanks to Xinerama, which no other distro to date has been able to do. This gives me a little more screen real estate to work with, which helps out a lot when I'm developing a Web site or working in an IDE. The package managers are slow, but how often do you really install software anyway?

Again, we'll just have to see how things pan out. Let's hope it turns out to be a positive experience.

Using Django to Design Your Database Schema

Last night I had a buddy of mine ask me how I would approach a particular database design problem. I get similar questions quite often from my peers--suggests there is something important lacking from the database classes out there. Instead of answering him directly, I decided to come up with this tutorial for using Django to design your database schema.

For those of you new to Django, this article might seem a bit advanced. In time I will have more introductory-level Django tutorials, but I hope this one is easy enough.

Create a Django Project

The first step is to create a Django project. If you already have a project that you can play with, you can skip this step. To create a project, go to a place where you want to keep your code (like C:\projects or /home/me/projects) in a command prompt/terminal and run the following command:

django-admin.py startproject myproject

This will create a new directory in your current location called myproject (you can replace myproject with whatever you'd like so long as you're consistent). This new directory will contain a few files:

  • __init__.py
  • manage.py
  • settings.py
  • urls.py

If you get an error message when running the above command, you might not have Django installed properly. See Step-by-Step: Installing Django for details on installing Django.

Create An Application

Once you have a Django project setup, you should create a new application.

Note: If you're using Windows, you will probably need to omit the ./ on the ./manage.py commands. I will include them here for everyone else who's using Linux or a Mac.

cd myproject
./manage.py startapp specialapp

This will create a new directory in your myproject directory. This new directory will contain three files: __init__.py, models.py, and views.py. We are only concerned with the models.py file in this article.

Create Your Models

Models are usually a direct representation of what your database will be. Django makes creating these models extremely easy, and Python's syntax makes them quite readable. The Django framework asks for models to be defined in the models.py file that was created in the last step. Here's an example (for my buddy who prompted the creation of this article):

from django.db import models

class Component(models.Model):
    item_number = models.CharField(max_length=20)
    name = models.CharField(max_length=50)
    size = models.CharField(max_length=10)
    quantity = models.IntegerField(default=1)
    price = models.DecimalField(max_digits=8, decimal_places=2)

class Project(models.Model):
    name = models.CharField(max_length=50)
    components = models.ManyToManyField(Component)
    instructions = models.TextField()

(for more information about models, see the Django Model API Reference)

I don't know about you, but that code seems pretty straightforward to me. I'll spare you all the details about what's going on (that can be a future article).

Install Your New Application

Once you have your models setup, we need to add our specialapp to our list of INSTALLED_APPS in order for Django to register these models. To do that, open up settings.py in your myproject directory, go to the bottom of the file, until you see something like

1
2
3
4
5
6
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
)

When you find that, add your specialapp to the list

1
2
3
4
5
6
7
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'specialapp'
)

Setup Your Database

Now you need to let Django know what kind of database you're using. Django currently supports MySQL, SQLite3, PostgreSQL, and Oracle natively, but you can get third-party tools that allow you to use other database (like SQL Server).

Still in your settings.py, go to the top until you see DATABASE_ENGINE and DATABASE_NAME. Set that to whatever type of database you are using:

DATABASE_ENGINE = 'sqlite3'
DATABASE_NAME = 'myproject.db'

Save your settings.py and go back to your command prompt/terminal.

Get Django's Opinion For Your Schema

Make sure you're in your myproject directory and run the following command:

./manage.py sqlall specialapp

This command will examine the models that we created previously and will generate the appropriate SQL to create the tables for your particular database. For SQLite, we get something like this for output:

BEGIN;
CREATE TABLE "specialapp_component" (
      "id" integer NOT NULL PRIMARY KEY,
      "item_number" varchar(20) NOT NULL,
      "name" varchar(50) NOT NULL,
      "size" varchar(10) NOT NULL,
      "quantity" integer NOT NULL,
      "price" decimal NOT NULL
)
;
CREATE TABLE "specialapp_project" (
      "id" integer NOT NULL PRIMARY KEY,
      "name" varchar(50) NOT NULL,
      "instructions" text NOT NULL
)
;
CREATE TABLE "specialapp_project_components" (
      "id" integer NOT NULL PRIMARY KEY,
      "project_id" integer NOT NULL REFERENCES "specialapp_project" ("id"),
      "component_id" integer NOT NULL REFERENCES "specialapp_component" ("id"),
      UNIQUE ("project_id", "component_id")
)
;
COMMIT;

Notice how Django does all sorts of nifty things, like wrapping the table creation queries in a transaction, setting up indexes, unique keys, and defining relationships between tables. The output also offers a solution to the original problem my buddy had: an intermediate table that just keeps track of relationships between projects and components (the specialapp_project_components table).

Notice that the SQL above may not work with database servers other than SQLite.

Enhancing The Intermediate Table

After my buddy reviewed this article, he asked a very interesting and valid question: What if a project needs 3 of one component? In response, I offer the following models (this requires a modern version of Django--it doesn't work on Django 0.96.1 or earlier):

from django.db import models

class Component(models.Model):
    item_number = models.CharField(max_length=20)
    name = models.CharField(max_length=50)
    size = models.CharField(max_length=10)
    quantity = models.IntegerField(default=1)
    price = models.DecimalField(max_digits=8, decimal_places=2)

class Project(models.Model):
    name = models.CharField(max_length=50)
    components = models.ManyToManyField(Component, through='ProjectComponent')
    instructions = models.TextField()

class ProjectComponent(models.Model):
    project = models.ForeignKey(Project)
    component = models.ForeignKey(Component)
    quantity = models.PositiveIntegerField()

    class Meta:
        unique_together = ['project', 'component']

Running ./manage.py sqlall specialapp now generates the following SQL:

BEGIN;
CREATE TABLE "specialapp_component" (
    "id" integer NOT NULL PRIMARY KEY,
    "item_number" varchar(20) NOT NULL,
    "name" varchar(50) NOT NULL,
    "size" varchar(10) NOT NULL,
    "quantity" integer NOT NULL,
    "price" decimal NOT NULL
)
;
CREATE TABLE "specialapp_project" (
    "id" integer NOT NULL PRIMARY KEY,
    "name" varchar(50) NOT NULL,
    "instructions" text NOT NULL
)
;
CREATE TABLE "specialapp_projectcomponent" (
    "id" integer NOT NULL PRIMARY KEY,
    "project_id" integer NOT NULL REFERENCES "specialapp_project" ("id"),
    "component_id" integer NOT NULL REFERENCES "specialapp_component" ("id"),
    "quantity" integer unsigned NOT NULL,
    UNIQUE ("project_id", "component_id")
)
;
CREATE INDEX "specialapp_projectcomponent_project_id" ON "specialapp_projectcomponent" ("project_id");
CREATE INDEX "specialapp_projectcomponent_component_id" ON "specialapp_projectcomponent" ("component_id");
COMMIT;

As you can see, most of the SQL is the same. The main difference is that the specialapp_project_components table has become specialapp_projectcomponent and it now has a quantity column. This can be used to keep track of the quantity of each component that a project requires. You can add however many fields you want to this new intermediate table's model.

Using This SQL

There are several ways you can use the SQL generated by Django. If you want to make your life really easy, you can have Django create the tables for you directly. Assuming that you have specified all of the appropriate database information in your settings.py file, you can simply run the following command:

./manage.py syncdb

This will execute the queries generated earlier directly on your database, creating the tables (if they don't already exist). Please note that this command currently will not update your schema if the table exists but is missing a column or two. You must either do that manually or drop the table in question and then execute the syncdb command.

Another option, if you want to keep your DDL(Data Definition Language) in a separate script (maybe if you want to keep it in some sort of version control) is something like:

./manage.py sqlall specialapp > specialapp-ddl-080813.sql

This just puts the output of the sqlall command into a file called specialapp-ddl-080813.sql for later use.

Benefits of Using Django To Create Your Schema

  • Simple: I personally find the syntax of Django models to be very simple and direct. There is a comprehensive API that explains and demonstrates what Django models are capable of.
  • Fast: Being that the syntax is so simple, I find that it makes designing and defining your schema much faster than trying to do it with raw SQL or using a database administration GUI.
  • Understandable: Looking at the model code in Django is not nearly as intimidating as similar solutions in other frameworks (think about Java Persistence API models).
  • Intelligent: Using the same model code, Django can generate proper Data Definition Language SQL for several popular database servers. It handles indexes, keys, relationships, transactions, etc. and can tell the difference between server types.

Downfalls of Using Django To Create Your Schema

  • The Table Prefix: Notice how all of the tables in the SQL above were prefixed with specialapp_. That's Django's safe way of making sure models from different applications in the same Django project do not interfere with each other. However, if you don't plan on using Django for your end project, the prefix could be a major annoyance. There are a couple solutions:
    • A simple "search and replace" before executing the SQL in your database
    • Define the db_table option in your models
  • Another Technology: Django (or even Python) may or may not be in your organization's current development stack. If it's not, using the methods described in this article would just become one more thing to support.

Other Thoughts

I first thought about doing the things mentioned in this application when I was working on a personal Java application. I like to use JPA when developing database-backed applications in Java because it abstracts away a lot of the database operations. However, I don't like coming up with the model classes directly, so I usually reverse engineer them from existing database tables.

Before thinking about the things discussed in this article, I created the tables by hand, making several modifications to the schema before I was satisfied with my JPA models. This proved to be quite bothersome and time-consuming.

After using Django to develop my tables, the JPA models turned out to be a lot more reliable, and they were usually designed properly from the get-go. I haven't created tables manually ever since.

If you find yourself designing database schemas often, and you find that you have to make several changes to your tables before you/the project requirements are satisfied, you might consider using Django to do the grunt work. It's worked for me, and I'm sure it will work for you too.

Good luck!

Step-by-Step: Installing Django

Being the Django and Python zealot that I am, I often find myself trying to convert those around me to this awesome development paradigm. Once I break them, these people often ask me a lot of questions about Django. Over the past few months I've noticed that one of the biggest sticking points for people who are new to Django is actually getting it up and running to begin with. In response, this is the first in a series of articles dedicated to getting Django up and running.

What is Django?

The Django Web site describes Django as "a high-level Python Web framework that encourages rapid development and clean, pragmatic design." Basically, Django is just about the most amazing thing for Web development. I have tinkered with several different Web technologies, but nothing seems to even come close to what Django can do for me.

What is Python?

Python is a programming language used in numerous aspects of computing these days. It has a very simple yet powerful syntax. It's an easy language for beginners to pick up, but it provides adequate levels of power for the more experienced developers out there. If you have never programmed anything before, or you have dabbled with something like BASIC, Python should be fairly straightforward. If you are a programming veteran, but have only worked with languages like C, C++, Java, etc, you might struggle a bit with the syntax of the language. It's not difficult to overcome the differences in a couple hours of hands-on development.

Let's get started.

Installing Python...

Having Python installed is critical--Django does not work without Python. I'm guessing that you're relatively familiar with the procedures for installing software packages on your particular operating system. However, I will share a few notes to point you in the proper direction if you're lost. If nothing else, just head over to the Python download page to download anything you need to install Python. I whole-heartedly recommend using the latest stable version of Python for Django, but you should be able to get by with as early a version as 2.3.

...On Windows

Simply grab the latest version of the Python installer. It is currently version 2.5.2. Once the installer has downloaded successfully, just run through the installation wizard like any other setup program.

...On Mac OS X

Recent Mac OS X computers come with Python pre-installed. To determine whether or not you actually have it, launch the Terminal (Applications > Utilities > Terminal) and type python -c "import sys; print sys.version". If Python is already installed, you will see the version you have installed. If you have a version that is less than 2.3, you should download the newest version. If you don't have Python installed, you will get a "command not found" error. If you're in this boat, just download the latest version of the Python Universal installer and install it.

...On Linux

Most Linux distributions also have Python pre-installed. Just like with Mac OS X, you can check to see by opening up a terminal/konsole session and running the command python -c "import sys; print sys.version". If you have Python installed, you will see its version. If you get an error message when running that command, or you have a version earlier than 2.3, you need to download and install the latest version of Python.

If you're running a Debian-based distribution (like Ubuntu, sidux, MEPIS, KNOPPIX, etc), you can probably use sudo apt-get install python to get Python. If you're running an RPM-based Distribution, you can probably use something like Yum or YaST to install Python.

A sure-fire way to install Python on any Linux system, however, is to install from source. If you need to do this, you simply:

  1. download the source for the latest version of Python
  2. extract it: tar jxf Python-2.5.2.tar.bz2
  3. go into the newly-extracted directory: cd python-2.5.2
  4. configure it: ./configure
  5. compile it: make
  6. install it: make install

(I've only installed Python from source one time, so I might be wrong)

Setting Up Your PYTHONPATH...

Generally speaking, if you didn't have Python installed before starting this tutorial, you will need to setup your PYTHONPATH environment variable. This is a variable that lets Python know where to find useful things (like Django).

...On Windows

  • Open up your System Properties (Win+Break or right click on "My Computer" on your desktop and select Properties)
  • Go to the "Advanced" tab
  • Click the "Environment Variables" button
  • If you have permission to change system variables, click the "New" button in the bottom pane. Otherwise, create the PYTHONPATH variable for your user account using the "New" button in the top (User variables for [username]) pane.
  • Set the variable name to PYTHONPATH
  • Set the variable value to C:\Python25\Lib\site-packages (replace C:\Python25\ with whatever it is on your system if needed)
  • Save it

You may also need to add the python executable to your PATH. If you can successfully run python from a command prompt window, you don't need to worry about it.

If you can't run python from a command prompt, follow the procedure above, but use the PATH variable instead of PYTHONPATH. PATH most likely already exists, so you just need to append/prepend the existing value with something like C:\Python25\ (again, this might need to change depending on where you installed Python)

...On Mac OS X

Your PYTHONPATH should already be setup for you.

...On Linux

Usually you just need to edit your ~/.bash_rc script to setup your PYTHONPATH environment variable. Go ahead and open that up in your preferred text editor and make sure there's something in it like:

export PYTHONPATH=/usr/lib/python2.5/site-packages:$PYTHONPATH

Save any changes necessary and run the following command:

source ~/.bash_rc

This will take care of updating your current session with any changes you made to your ~/.bash_rc.

Installing Django

Once you have Python and have verified that you have version 2.3 or later, you are ready to install Django. Currently, the latest stable release is 0.96.1, but this is grossly out-dated. Django 1.0 will be released on September 2nd 2008, so the "unstable" copy of Django is pretty close to what 1.0 will have to offer. There are some incredibly useful improvements in the unstable version that I don't think I could do without anymore, so that's what I'll talk about installing here.

First, you need to have a subversion client. On Windows, the most popular one is called TortoiseSVN. On Mac OS X, I have played with a few, but I think Versions is a pretty decent one. Linux also has several to choose from, but if you're using Linux, you're probably going to use the command line anyway (right?).

For brevity, I will just use the subversion commands necessary to accomplish this task (instead of discussing all GUI interfaces to subversion).

The exact location that Django should be installed differs from system to system, but here are some guidelines for typical setups:

  • Windows: C:\Python25\Lib\site-packages
  • Linux: /usr/lib/python2.5/site-packages
  • Mac OS X: /Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/site-packages

If you want a definite location, run the following command:

python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"

Once you know that location, go there in your command prompt or terminal session. Then execute this command svn co http://code.djangoproject.com/svn/django/trunk/django django. You will see loads of output, showing all of the files that you are downloading as you install Django.

As soon as that process completes, you should run python -c "import django" to make sure everything worked properly. If the command doesn't display an ImportError, you're good. Otherwise, you need to try again.

Getting Access to Django Scripts...

Once you can successfully import django, you might want to make sure you can run the django-admin.py script that comes with Django.

...On Windows

This process is very similar to what we did with the PYTHONPATH environment variable earlier.

  • Open your System Properties again
  • Go to the Advanced tab
  • Click the Environment Variables button
  • Find your PATH environment variable (either for your user or system-wide)
  • Make sure that the variable value contains something like C:\Python25\Lib\site-packages\django\bin
  • Save any changes
  • Open a fresh command prompt
  • Try to run django-admin.py. If you're successful, you're ready to get started with Django. Otherwise, you need to fix your path to django/bin or just call the django-admin.py script using an absolute path when needed.

...On Mac OS X

You can run a command similar to this:

sudo ln -s /Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/site-packages/django/bin/django-admin.py /usr/local/bin

...On Linux

If you have "root" privileges on your Linux system, you can execute a command like:

sudo ln -s /usr/lib/python2.5/site-packages/django/bin/django-admin.py /usr/local/bin

If you don't have "root" privileges, you can setup your own /usr/local/bin:

mkdir ~/bin

Make sure your ~/.bash_rc contains something like:

export PATH=$HOME/bin:$PATH

Then update your current session with any changes you made to ~/.bash_rc by running this command:

source ~/.bash_rc

And that should do it! Now you should be ready to get started with Django.

Feel free to leave a comment if you're having problems installing Django. Good luck!

Check out Installing Django on Shared Hosting.

PyScriptures 0.2a Is Here!

So, for those of you who aren't in my immediate vicinity or who aren't on Google Talk all the time, this might well be your first exposure to my latest project. I'm calling it PyScriptures. Py because I wrote it all in Python. Scriptures because it is a program that provides the entire LDS standard works (as far as the actual scriptures are concerned, anyway).

Some History

(feel free to skip to the good stuff if you don't care about history, or skip to the downloads)

I have been working on this sort of program for a very long time. My first attempt was way back in probably 2001, using PHP. I wanted to have a way to easily read the scriptures on my computer without requiring an Internet connection. To go along with this, I wanted to be able to highlight text, mark verses, and easily navigate the scriptures. Obviously, I never quite got it right--that's why I'm still working on new versions all the time.

After a while, I learned that I would be getting a nifty Sharp Zaurus SL-5500 as a graduation present. It's a Linux-based PDA (one of the first, actually), and it's still pretty powerful considering that it's 6 years old now. Anyway, once I got that little gadget, I wanted to get the scriptures on there, too. I didn't have any of the MarkMyScriptures software that other PDA users enjoyed, because of the different operating system. Not to mention how cheap I am (I hate buying software). I ended up porting my PHP/MySQL version of the scriptures to my PDA and using it that way for a little while, but that proved to be very inefficient. The project went on hold for a while, during my time as a missionary in Romania.

When I returned home from my mission, I picked up the scriptures project again. I think my next stab was a Swing-based Java application. It worked well enough, but it never really got too far beyond, "Oh look! The scriptures!"

It was also during the time I was working on the Java version that I realized that the database I was relying upon for my scriptures was incomplete. I'm not sure what the extent of the missing information was, but I remember specifically looking up Doctrine & Covenants 77 only to find questions with no answers. The database was also not very "normalized" but that's more of a nerdy topic, so I will spare you the details. I attempted to contact the bloke in responsible for maintaining that database to let him know of the problems, but it seems like he died or something. Absolutely no response from him, and no activity on his website for two years.

After discovering the lack of complete scripture in that database, I made a promise to myself that I would make my own version of the database so I wouldn't have to stumble upon more incomplete or inaccurate scriptures. This became a reality early in May, as I wrote a program (in Python) that actually downloaded (I call it "harvesting") all of the scriptures directly from the Church's website. It took quite a bit of time to perfect, but as far as I can tell, it works great now. It puts all of the scriptures in a nice, normalized database. So far I know it works with SQLite and MySQL, but it should work just dandy with others as well.

Once I had that fresh database, I began working on a graphical interface for the scriptures. I had been tinkering with something called wxPython for a little while, but I'd never really built anything useful with it. I could never get used to laying things out after using the amazing GUI builder in NetBeans.

This past weekend I've been hacking nearly non-stop to get a nice, functional interface for my scripture program. I'm very satisfied with it, and I have to admit that it performs far better than any previous iteration of this project. There's still a lot to be done to make it work the way I want it to, but here's a brief list of features in this version 0.2a release:

Features Include:

  1. Cross-Platform Compatible: This program works exactly the same on Windows, Linux, and Mac. I've tested it on Windows XP, Vista, Ubuntu Linux, Slackware Linux, and MacOS X (leopard) and have only found minor differences that don't really matter anyway. The program itself does work though.
  2. Fast: Python does a good job at working quickly, even with my crummy code. It boasts incredible speed when retrieving and rendering the entire canon of scripture.
  3. Simple searching: You can type in a word, part of a word, or a whole phrase, and it will find any and all matches (case-insensitively) in the entire standard works.
  4. Quick Jump: Know the exact reference to the scripture you want? Type it in and you're immediately taken to that verse. I never understood why other programs don't have this feature. My implementation is not perfect, but it sure as heck didn't take much to get it where it is.
  5. Adjustable font sizes: You can easily adjust the size of the scripture text (within reasonable limits). That way you can make it easier to read if you're not sitting right in front of your computer.
  6. Easy navigation: You can quickly and easily jump to the next or previous chapter or book. I realize that this might not be very useful to a lot of people, but I love this sort of functionality.
  7. Random verse: Click one button to jump to some random verse anywhere in the scriptures. This is mostly a database deal, and it seems to prefer the Old Testament in my experience. Maybe that's just because the Old Testament probably has more verses than the rest of the volumes put together?
  8. Good memory: Prefer to have your window maximized? Don't like seeing the toolbar? The program will remember things like that, as well as the size and position of the window on your screen (if it's not maximized) and what verse you had selected immediately before closing down the program.
  9. Keyboard shortcuts: For those of us who hate to use mice, there are keyboard shortcuts to do most things in the program.

There's still more fun stuff to come, but I had to get something out the door. I spent most of today just trying to get the program to behave well on other platforms (mostly Windows), because I develop on Linux. If you're interested in trying out what I have now, feel free to download whatever suits you best:

Downloads

Windows Installer (32-bit) (9.0MB)

Debian Linux (including Ubuntu) (2.9MB)

Launch pyscriptures after installing and it should work.

MacOS X (11.3MB)

Man... Gotta love the size differences.

Requirements

This program requires Python 2.4+, pysqlite2 (or sqlite3 if you have Python 2.5), and wxPython 2.8+. These may be different, but that's what I used to develop with, so I know it works with them. The Windows installer should include everything you need to get started, as should the Mac installer.

Note: The .dmg is very, very shabby right now. I plan on making it prettier as time goes on, but this _is_ an alpha release, after all. You can't expect too much.

I should stop here. Enjoy!

Slackware 12.1 on an Asus EeePC 701

Attention!

This article has a follow-up for Slackware 12.2.

The following are the steps I took to install Slackware 12.1 on my EeePC this past weekend. I hope you find them complete and helpful!

Installing Slackware 12.1 on an Asus EeePC 701

  1. Burn DVD .iso to disc
  2. Turn on EeePC
  3. Hit F2 to run setup
  4. Go to the Advanced tab, and set "OS Installation" to "Start"
  5. Go to the Boot tab, and ensure that the external DVD drive will be used for booting before the internal SSD
  6. Exit and save changes
  7. Just hit enter after rebooting from BIOS configuration when the Slackware boot screen shows up
  8. Unless you want to use a different keymap for whatever reason, hit enter when asked to select a keyboard map
  9. Login as root
  10. Run fdisk or cfdisk on /dev/hdc
  11. Remove all partitions (unless you know what you're doing)
    1. fdisk: d to delete (you may have to select multiple partitions to delete if you have more than one for some reason)
    2. cfdisk: Select all partitions individually with up/down arrow keys and use the left/right arrow keys to select delete from the menu at the bottom. Hit enter to run the delete command when it's highlighted.
  12. Create one partition that takes the whole SSD (again, unless you know what you're doing)
    1. fdisk: n (for new); enter; p (for primary); enter; 1 (for the first primary partition); enter; enter (to start at the beginning of the drive); enter (to select the end of the drive)
    2. cfdisk: Select the new command with the left/right arrow keys and hit enter when it's selected. Make it a primary parition, and have it take the whole SSD (3997.49MB in my case).
  13. Set the type of the new partition to be Linux
    1. fdisk: t (for type); enter; 83 (for Linux); enter
    2. cfdisk: Use the left/right arrow keys to select the type command at the bottom and hit enter when it's selected. Choose 83.
  14. Set the new partition (or the first, if you decided to make more than one) to be bootable
    1. fdisk: a (for bootable); enter; 1 (for primary partition 1); enter
    2. cfdisk: Select the bootable command from the bottom using the left/right arrow keys. Hit enter when it's selected.
  15. Write the changes to the partition table and quit
    1. fdisk: w
    2. cfdisk: Use the left/right arrow keys to select the write command from the bottom. Hit enter when it's selected. Type 'yes' to verify your intent, acknowledging that your previous data will be "gone". Then select the quit command.
  16. Run setup
  17. Select TARGET to specify where you will be installing
  18. Select /dev/hdc1
  19. Format the partition
  20. To reduce write cycles, many people suggest formatting with ext2, which is a non-journaling filesystem. However, many people claim that the limited number write cycles of SSD is not something to worry about. Use your best judgement on this one. Hit OK after the format is complete.
  21. Select where you plan to install Slackware from. In my case, it's the DVD. I usually tell it to find the media automatically. Select manual if you know which device your DVD drive is. Mine was /dev/sr0.
  22. Select the packages you wish to install. This is where your installation will likely differ greatly from mine because of personal preferences. I do a lot of development, so I will keep a lot of things for that. Here's what I selected to install:
    1. Base Linux System
    2. Various Applications that do not need X
    3. Program Development (C, C++, Lisp, Perl, etc.)
    4. Linux kernel source
    5. Qt and the K Desktop Environment for X
    6. System Libraries (needed by KDE, GNOME, X, and more)
    7. Networking (TCP/IP, UUCP, Mail, News)
    8. Tcl/Tk script languages
    9. X Window System
    10. X Applications
    11. Games
  23. Choose whether or not you want to be picky about your software. To save a little extra disk space, I'm going to manually choose what I don't want. This includes:
    1. A: cpio, cryptsetup, cups, floppy, genpower, jfsutils, mdadm, mt-st, mtx, quota, reiserfsprogs, rpm2tgz, tcsh, xfsprogs
    2. AP: amp, cdparanoia, hplip, gutenprint, jed, joe, jove, ksh93, mysql, rpm, xfsdump, zsh
    3. D: gcc-gfortran, gcc-gnat, gcc-java, mercurial, p2c
    4. N: elm, epic4, httpd, mailx, mutt, netatalk, pine, popa3d, proftpd, rp-pppoe, samba, slrn, tin, trn, vsftpd
    5. TCL: hfsutils
    6. X: anthy, bdftopcf, beforelight, libhangul, sazanami-fonts-ttf, sinhala_lklug-font-ttf, tibmachuni-font-ttf, wqy-zenhei-font-ttf
    7. XAP: audacious, audacious-plugins, gftp, mozilla-thunderbird, pan, seamonkey
  24. Wait for the installation to complete. It took almost a full hour with my package selection, leaving me with 485.4MB free on my 4GB SSD.
  25. Choose whether or not you want to make a bootable USB... I skipped it.
  26. Choose how you wish to install LILO. I chose simple.
  27. Choose your frame buffer mode for the console. I chose 640x480x256.
  28. Specify any optional kernal parameters. I left this blank, originally, but later learned that having 'hdc=noprobe' increased my disk access speed by about 13 times.
  29. Specify whether you wish to use UTF-8 on the console. I chose no.
  30. Specify where to install LILO. I chose MBR.
  31. Specify your mouse type. I chose imps2.
  32. Specify whether or not you wish to have gpm run at boot, which allows you to use your mouse in the console. I chose yes.
  33. Configure your network.
  34. Give your eeepc a hostname. This can be whatever you'd like.
  35. Specify the domain for your network. This can be whatever you'd like as well.
  36. Configure your IP address information. I just chose DHCP.
  37. Set the DHCP hostname. I left this blank.
  38. Review and confirm your network settings.
  39. Choose which services you wish to have running immediately after booting.
  40. See if you want to try custom screen fonts. I usually don't bother.
  41. Specify whether your hardware clock is set to local time or UTC.
  42. Choose your timezone.
  43. Select your preferred window manager. I chose KDE.
  44. Set the root password.
  45. Slackware has been installed! Exit the setup program and reboot.
  46. Hit F2 to enter the BIOS again.
  47. Set OS Installation to "Finished" and exit the BIOS, saving changes.
  48. Reboot into Slackware! The first boot takes a while because of all the initial setup. It is faster on subsequent reboots, assuming you don't add new services (like apache and mysql) at boot.

Change a few settings around.

  1. vi /etc/inittab
  2. (set default runlevel to 4)
  3. vi /etc/lilo.conf
  4. add 'compact' somewhere to make it boot faster
  5. change the boot delay so it's not 120 seconds

Now for installing various drivers.

  1. Install the ethernet driver: http://people.redhat.com/csnook/atl2/atl2-2.0.4.tar.bz2
    1. wget http://people.redhat.com/csnook/atl2/atl2-2.0.4.tar.bz2
    2. tar jxf atl2-2.0.4.tar.bz2
    3. cd atl2-2.0.4
    4. make
    5. cp atl2.ko /lib/modules/2.6.24.5-smp/kernel/drivers/net/
    6. depmod -a
    7. modprobe atl2
    8. ifconfig
  2. Install the drivers for the wireless: http://snapshots.madwifi.org/special/madwifi-nr-r3366+ar5007.tar.gz
    1. wget http://snapshots.madwifi.org/special/madwifi-nr-r3366+ar5007.tar.gz
    2. tar zxvf madwifi-nr-r3366+ar5007.tar.gz
    3. cd madwifi-nr-r3366+ar5007.tar.gz
    4. scripts/madwifi-unload
    5. scripts/find-madwifi-modules.sh uname -r
    6. make && make install
    7. modprobe ath_pci

I kind of stopped taking notes after I realized how much fun it was to have Slackware on my EeePC. If you have questions, just add a comment below.

Why I Like Python

For the past 8 years or so, I've been very much involved with programming using the PHP scripting language. It is a powerful scripting language that suits building websites very well. PHP has a huge set of useful built-in functions, and more recent versions support object-oriented programming. I first started teaching myself PHP when I got tired of having to build each and every web page on my site manually. I hated having to change dozens of web pages just because I added a new link to my navigation. All sort of reasons like this prompted me to investigate PHP. Little did I know then that this language would occupy so much of my time in the future.

I rapidly learned that PHP offered much more than just allowing me to update one part of my website to change all pages. I started tinkering with all aspects of what PHP offered, and I'm still learning about it. After many years of searching, I finally found a programming language that was easy, fast, and efficient for my needs.

Through the years, I continued to develop various applications using PHP. I attempted to write my own forum/bulletin board software while I was still in high school. If I may say so myself, the forum really had some awesome concepts behind it. But my problem was that I lost interest too fast. I also built a very large application that reduced a 1.2GB MS Access database down to less than 15MB using PHP and MySQL. The new application offered many enhancements over the previous system. For one thing, it was much faster. Second, it allowed multiple simultaneous users to modify the database. Three, so far it has lasted more than 3 years, compared to the 1 year maximum that the MS Access solution always seemed to hit before it crashed.

Using PHP, I helped revolutionize the way one of the companies I work for developed websites. I built a simple in-house web framework that supposedly reduced development time by allowing us to forget about the mundane details involved in virtually every website and just get to the developing. In a matter of two weeks (with a full class load and another job), I managed to write an e-commerce solution for the same company using PHP.

Basically, PHP has treated me well over the years. But this post is not supposed to be about PHP. If that's the case, why have I rambled about PHP this whole time, you ask? Well, it's mostly to demonstrate that I have a lot of experience with the language. I have a pretty good feel for what it's capable of and how I can accomplish most anything I need.

With all of that in mind, I've encountered my frustrations with PHP. They may seem petty and moot to most people, but they have turned out to be the determining factor in what scripting language I prefer. Here is a short list of things I now despise about PHP:

  • dollar signs ($) to signify variables -- while this is a useful feature, it becomes quite bothersome when you're programming all day long (at least it does for me). I'll get to why later.
  • using an actual arrow (->) to access attributes -- most other modern programming languages simply use a period (.) for this functionality. I'll comment more on this and why it frustrates me later as well.
  • lack of true object-oriented constructs -- in other object-oriented languages, like Java, if you have a string and you want to determine its length, you call the length() method of that string. In PHP, you call a function such as strlen($var). This sort of behavior plagues the language.
  • too many unnecessary keystrokes -- as I mentioned before, all mutable variables are preceded by a dollar sign ($). That is 2 keystrokes (shift and 4) every time you want to refer to a variable, wheres most languages nowadays have none). Likewise, accessing attributes of objects in PHP uses an arrow (->), which is three keystrokes (minus, shift, and .). Most other object-oriented languages only require a period (one keystroke) for such functionality. The main reason I make such a big deal out of the number of keystrokes is simple. The more keystrokes a program requires, the more likely you are to have bugs. The fewer keystrokes a program requires, the less likely it is that your program will be broken. It boils down to maintainability. Also associated with the number of keystrokes is the pure laziness within me and most other programmers.

These frustrations have been bothering me for several years now. I continued using PHP mostly because it's so widely supported, but also because I could not find a suitable replacement for it. I investigated a few others, but they apparently didn't have a great influence on me right now because I don't remember any names.

When the whole Ruby on Rails bandwagon was rolling through town, I decided to hop on to see what all of the hubbub was about. I started studying the Ruby script language, and I found that it had some really neat things about it. It uses a more solid approach to object-oriented programming, which I really liked. I also noticed that it employs some intriguing structures for accomplishing things in ways I've never seen before. Despite these things, Ruby still didn't seem like a viable replacement for my PHP. It didn't come up to snuff in performance in many cases, so I essentially abandoned it.

For at least a year now, I've been interested in learning Python. I've heard a lot about it over the years, but I just never seemed to make the time to actually sit down and study it. That is, not until about the beginning of August of 2007. After I made my decision that Ruby and Ruby on Rails weren't quite up to par for my needs, I stumbled upon the Django Project, which is a web framework similar to Ruby on Rails, only built using Python.

I decided this was my chance to actually sit down and learn a little about this "Python" so I could see what it had to offer. I mostly used Django as my portal to Python. As I started learning Django, I became more and more familiar with the way Python works and how I work with Python.

At some point in time, I decided that I actually liked Python, and my wife let me buy some really cool books to help me learn it. By the beginning of October 2007, I had convinced my supervisor at work to let me start building websites using Django instead of our home-grown PHP framework.

And here comes a story. This is the main reason I blabbered about my experience with PHP so much at the start of this article. Again, after all these years, I feel very confident that I can do just about anything I want efficiently and elegantly with PHP.

Back in October of 2006 (after using PHP for some 7 years), I was asked to write a PHP script to parse some log files and output various bits of information in a certain format. After maybe a week, I had a script that did the job fairly well. Most of the time it worked, but there were occasions when it didn't and I had to fix it. The script turned out to be 365 lines of code with very few comments scattered throughout. It's also a maintenance nightmare, even for me.

In October of 2007, I rewrote that same script in Python. After only a couple days, the script seemed to be perfect. It did its job, and it did it well. With comments for just about every single line of code, the Python version of the script took up a mere 118 lines of code. Take out the comments and it is 56 lines of code. The script is several times more understandable and maintainable than its PHP counterpart. I also believe that it is much more efficient at doing its task. Keep in mind that I had only been using Python for about 2 months at this point in time.

It's been through various experiences like the log parser that I have decided I prefer Python over PHP. Obviously, I'm not quite as comfortable with it as I am with PHP, but I don't feel too far behind. Now, less than 6 months after deciding that we'd use Django at work, I don't think my supervisor could be happier. Building a typical website with our PHP framework takes between 1 week and a couple months. Thanks to Python and Django, most of our websites can be "ready" within just a few hours. That time assumes that the website's design itself is ready for content to be put into it and also that the client does not require custom-designed applications.

Python and Django have helped revolutionize the way we do things at work, and I can hardly stop thinking about it. Python fixes nearly all of the frustrations I had with PHP. The frustrations it doesn't take care of are worth the sacrifice. Python is capable of object-oriented programming. It uses a period (.) to access object attributes. Variables are not preceded by some arbitrary symbol.

Also, the fact that Python code can be compiled to bytecode (like Java) is enormously beneficial. Each and every time a PHP script is executed, the PHP interpreter must parse the code. With Python, the first time a script is executed after an edit, the program is compiled to bytecode and subsequent executions are faster. That is because the bytecode is processed directly by the Python Virtual Machine (as opposed to being compiled to bytecode _each_ time and then executed). Python also offers a vast amount of standard library functions that I would really appreciate having in PHP. But from now on (at least for the foreseeable future), I will try to do all of my scripting in Python and leave PHP for the special cases.

How to Upgrade a NetBeans Project to JDK 6

NOTE: I am currently using NetBeans 5.5.

Here are the simple steps:

  1. Download and install JDK 6 if you haven't already
  2. Add the JDK 6 platform to NetBeans
    1. Tools > Java Platform Manager
    2. Add Platform
    3. Navigate to JDK 6's installation root directory. It will probably have a fancy overlay icon over it.
    4. Click Next
    5. Give the platform a name and continue onward
  3. Open a project in NetBeans
  4. Right-click on the project and open its properties
  5. Open the Libraries item on the left
  6. Change the Java Platform to your new 1.6
  7. Open the Sources item on the left
  8. Change the source level at the bottom to 1.6
  9. Enjoy!

There really are quite a few nice features in this release. So far I've found time to play with the built-in JTable sorting methods and the system tray icon stuff. It's so much easier than it used to be!!