Django on Dreamhost

Dreamhost LogoThere are many posts that outline the steps to deploy a Django app onto a Dreamhost shared server. This is my experience.

I used a couple of pages as references, both to help me know what to do and to learn what not to do. Installing django 1.3 on DreamHost on oscarcp.com helped with the virtualenv setup, and the Passenger and WSGI section on the Dreamhost Wiki page on Passenger showed how to use the correct python interpreter.

Python

I’ve previously setup the Python 2.6.5 interpreter to be my default on Dreamhost (see this post), so I’m not sure what version they offer currently as the default. Be sure that the version you are running is supported by your desired Django version.

Setup Passenger for you Domain

It’s an option on the Dreamhost panel. When editing your domain, check the box labeled “Passenger (Ruby/Python apps only)“. This will add a /public directory in the domain root. (It may take a few minutes for this work to finish.)

Install Virtualenv

There is a version of virtualenv available on Dreamhost which will work fine. However, I wanted the latest, so I downloaded and installed it. First, I opened a terminal window and ssh’ed to my Dreamhost account. I have a downloads folder setup, so I switched to that before running the download. (As of this writing, 1.6.4 is the latest released version.)

wget http://pypi.python.org/packages/source/v/virtualenv/virtualenv-1.6.4.tar.gz
tar -xvf virtualenv-1.6.4.tar.gz
python setup.py install

Create the Virtual Environment for the Project

My naming convention for virtual environments is to use the project name followed by ‘env’. Pretty creative, huh? Since my project is an art gallery, I called it ‘galleryenv’.

Before running the command, switch to the root directory for the domain. This is the folder containing the /public directory created by the Passenger selection earlier. The Dreamhost default is to name this the same as the domain, but yours may be different.

virtualenv envname --no-site-packages

The –no-site-packages argument is pretty important, as it instructs virtualenv to ignore all python packages installed in the base interpreter. Everything you need for the project should be installed in this new environment. This means that your project will be insulated from changes to the base install, and will allow different projects/environments to use different versions of the same packages.

Next, activate the interpreter in the virtual environment you just created. Basically, you’re switching to this new copy of Python rather than the default for your account.

source envname/bin/activate

Your command prompt should now be prefixed with the name of the environment.

Run Django Setup Script? I Say No

Dreamhost provides a script, django-setup.py, that will create your settings file with database settings and the passenger_wsgi.py file for your application. This works pretty well for a new project, but most of the work has to be backed out when an existing application using a different directory structure or settings is being setup. Since I develop my projects on a local development machine (as should you), this isn’t much help. Instead, there are just a few manual steps to perform that will get things ready to go.

Also, you’ll notice that we haven’t install Django yet. Since we used the –no-site-packages option when creating the virtual environment, any Django version installed on your account earlier will not be accessible while you are using the new interpreter. The Dreamhost script won’t run unless Django and the MySQL library for Python are installed. No worries! We’ll get to that soon enough.

Bring On the Source code

At this point, it is time to load the source for your Django project onto the server. My convention is to create a directory with the project name in the root directory for the domain – so mine would be called ‘gallery’. You can put these wherever you like, just remember the path for a later step.

I use the Subversion version control system, also provided by Dreamhost, to store my code, so I would use the ‘svn copy’ command to install my source code. Any VCS or DVCS is fine, or the age old FTP approach will work as well.

PIP and Requirements

I’ve just started using PIP recently, although it has been around the Python world for a while. PIP is a general Python install tool, and we’re going to use it to quickly install the libraries we need for our application. Since it is installed with virtualenv (at least in 1.6.4), it is already setup and ready to go.

Before this step, ensure that the virtualenv interpreter is still activated – see the source command above. The goal is to install the needed Python libraries only for this environment and not for use by the default Python install.

PIP offers the option to include a list of libraries to be installed in a batch run. Following convention, I’ve included a file with my application called requirements.txt. Here’s what mine looks like:

Django<1.4
PIL
django-photologue

First, I want to install the latest release of Django 1.3, so I ask for the most recent less than v1.4. Next, I need to use the Python Image Library, or PIL. (This library has operating system dependencies that will need to be satisfied before the install.) Finally, the package django-photologue is requested. Prepare your own requirements in this format.

To run PIP with your requirements, use the following syntax:

pip install -r requirements.txt

It's just that easy! Watch the output carefully for any errors or warnings.

Since I'm using MySQL for my database on the server, I also need the mysql-python library.

pip install mysql-python

Passenger Configuration

The django-setup.py script I mentioned earlier builds a file called passenger_wsgi.py that links Apache to the django application. For your install, you can either run the script, or use my example as a starting point. Either way, some edits will be needed to use the virtual environment. Here's is my file:

import sys, os
INTERP = os.path.join(os.environ['HOME'], 'gallery.gentryart.us', 'galleryenv', 'bin', 'python')
if sys.executable != INTERP:
    os.execl(INTERP, INTERP, *sys.argv)
sys.path.append(os.getcwd())
sys.path.append(os.path.join(os.getcwd(),'gallery'))
os.environ['DJANGO_SETTINGS_MODULE'] = "gallery.settings"
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

The second line of the script sets a variable (INTERP) to the path of the virtual environment's python interpreter. Basically, the os.path.join command builds the path using the directory names we created earlier. Starting at the home directory, we add in the domain name and the environment name, followed by 'bin/python'. Replace my gallery references with your application's directories.

Lines 3 and 4 will switch to the new python interpreter referenced by the variable.

The sys.path.append lines include our application directory in the search path used by Python, and the next line points to our settings.py file. In these lines, replace 'gallery' with the path to your application source code.

passenger_wsgi.py should be placed in the root directory for the domain. Feel free to copy my example and edit to taste.

Static Media

Better bloggers than I have written about serving static media, and this is just one of many ways to do it. Since I store these files (CSS, JS, images) in version control, they are installed in the application directory created earlier. A simple symbolic link will point Apache to the correct files.

First, switch the the /public directory located in the root directory for the domain. Then, setup the symbolic link to refer to the files. Since my files are located in a directory called 'static', this command takes care of it.

ln -s ../gallery/static static

Next, setup a link for the Django admin media.

ln -s ../galleryenv/lib/python2.6/site-packages/django/contrib/admin/media admin_media

One Last Thing

Since I had already been running my app on this domain using a pre-passenger method for hosting Django, I had a leftover (and hidden) .htaccess file that was messing things up. A quick note to the Happy Dreamhost Support Team and they found and fixed the problem. No .htaccess settings are needed for this type of configuration.

Ready To Go

Hopefully, everything is working for you at this point. In my case, Django came up and told me my database settings were wrong, but I see that as progress. The site is now working fine.

Good luck!

http://gallery.gentryart.us

Applications, Models, Properties, and Circular Imports

I’ve been fighting a problem in my Django project for a while, and the solution I came up with seemed like I was settling for practicality over elegance.  This post is an attempt to rationalize my choices.

First, I love the idea of reusable applications.  I have incorporated a few from open source, including django photologue for the gallery, and the ease of use was amazing.  While I know that my work may not be general enough for public use, I do hope to be able to reuse some code in different parts of my project.  And who knows, maybe something useful to the world will come from it.

So, I divided my project into what seemed to be mostly independent applications, and started coding.

My problem came when I started heavy use of the @property decorator in my models.  Being able to reference a function using the dot notation is very handy (even though they aren’t available in functions that reference fields internally – like the ORM).  However, I found myself referencing models and fields in these two certain applications often, and I ended up with a circular import error.

I’d like to take a moment to point out that my Internet search on circular imports did not yield much help.  Several sites merely pointed out that a circular import was the result of poor coding, but offered no advice.  Finally, one post suggested moving the import to the end of models.py.  This solved the problem in my laptop development environment, but the same code would not run on my Dreamhost server (maybe it is importing in a different order).

Next, I tried to rewrite my property functions to reference the models from the other application only through the ORM – to avoid the need for an import.  This helped on the less complicated functions, but I couldn’t do it for the really useful, complicated ones.

My solution was to combine these two applications together.  The two are obviously very connected, and it was easy to make the case.  However, one of them could have been used in a more general setting, and now it is too specific to be presented to the public.  I guess I’ll have to earn my open source cred with something else.

The important thing here is that I am past this and am once again able to work on features instead of back end problems.

Am I being too theoretical? Are properties not the way to go? The comment section is waiting for your thoughts.

Changing Hosts in Dreamhost

Dreamhost logoLast night, Dreamhost moved my account to a new host. Not a big deal, and it’s certainly understandable, but it did break my one running Django app – the Gentryart Gallery. Here’s what I did to fix it.

First, as suggested in the Dreamhost Server Moves Page, I recompiled Python to create a 64bit version. While I was at it, I upgraded from v2.5.2 to v2.6.5. This great page by Ryan Kanno outlined the steps. I just changed the version number. I also deleted my old installation of Python so there wouldn’t be any chance of confusion there.

Now, if I would have used the same python version and kept everything in the same locations, I would have been done. Since I didn’t, I had a little more to do.

Next, I needed to install all of the python packages I use. Here’s the list:

Following the famous Jeff Croft post, I updated the admin_media shortcut to point to the new Django files.

And the gallery is back in business!

Django and Dreamhost

Last month, Dreamhost announced their new support for Django using Passenger WSGI. (Instructions are posted here.)

The process is pretty simple. First, I setup a new subdomain for the app. One can probably setup a Django app using a subfolder of an existing domain, but I haven’t explored that yet. As instructed, I setup the subdomain with the Passenger option.

Next, I added a database for the domain. Nothing tricky or new here.

The final step in their instructions was to download and run the setup script. I was a little confused about where I should run it, as the instructions referred to the “directory for the application”. After a little experimentation, I figured out that I should be in the root directory for the subdomain (created when the subdomain was created), which has the /public directory in it. The script asks a few questions and runs quickly. At the end, I had a settings.py and urls.py, the database was initialized, and the admin app was ready to go. If the programmer is starting a new application, this is a good place to start.

However, I have already been working on my app on my laptop, and I wanted to copy it to the server. Since I’m using Subversion to store my code, it was easy to use the svn co command to copy the files over – after I first moved the recently created settings.py to a safe location.

I copied the database and media location entries from the new settings.py into the existing version with my application. I typed in the URL and was ready to go.

EXCEPT … I forgot about a problem I hit when installing another Django app on Dreamhost using the older FastCGI configuration. It seems that I have to qualify a couple of the file names in settings.py and urls.py to help Django find them. Specifically, the applications listed in INSTALLED_APPLICATIONS need to have the project name appended to the beginning. The same for imported URL files. Those two changes did the trick.

UPDATE: To solve this path problem, I added this line to passenger_wsgi.py:

sys.path.append(os.path.join(os.getcwd(),'project')) - where 'project' is the project name

EXCEPT … my application uses Python constructs (in this case, decorators) that were added after version 2.3, which is the default on my Dreamhost server. With my earlier Django install, I had installed Python 2.5 in my /bin folder and was able to directly the server to use that version. Even after following the instructions I found in a discussion attached to the wiki page, I was not able to get this to work. The command I added to pasenger_wsgi.py was:

if sys.version < "2.5": os.execl("/home/gentryadmin/bin/python", *sys.argv)

Instead of using the new version, I see a screen telling me I have hit an Internal Server Error. I've yet to find a log file that has information on this error.

I can change the decorator code to Python 2.3 friendly syntax, but this problem will get in the way when/if I want to use other Python/Django applications or modules. Hopefully, I can get this fixed.

Any ideas or thoughts? Please leave a comment below.

Getting Things Done to GTD (Jabber with Google Apps)

This is a complicated process I followed to try to make things easier. It started with an article discussing how to Make Gmail Your Gateway to the Web. Basically, he is trying to make his GMail account his gateway to everything. I’ve got my Google Apps account all setup to received email from every account with filters and tags and alternate accounts. The calendars are shared with the rest of the family (if I could only get everyone else to use them).

The only thing he’s done that I haven’t is what he calls “update and track your social networks via IM”. So, I setup the ping.fm and notify.me accounts as he describes, and tried it out. It all worked pretty well except I coulnd’t get the notify.me account to validate GTalk.

A little Google research and I found that I have to add 10 SRV entries in my DNS for the domain to property route the jabber messages to Google. This Google article explains it pretty well. Next, I had to figure out how to enter this info into a Dreamhost account. I found that the correct method is to enter “_xmpp-server._tcp” in the name field and “5 0 5269 xmpp-server.l.google.com.” in the value field (be sure to include the period at the end). After a little time for the DNS to get settled, I tried the validate process again, and it worked great.

OK, so after all of that, let’s try it out.

Ping.fm works exactly as advertised. I setup micro-blog messages to go to Twitter, and status updates to both Twitter and Facebook. It all works via the chat client in GMail.

Notify.me also did what I expected. I couldn’t find a way to get my entire Twitter stream to come through, but the messages sent to me came through fine. (Still working on direct messages)

However, there is a problem. In the IM that comes in from notify.me, I can’t tell who sent the message. There isn’t any setup of the format that I can see, but it wasn’t there. I posted a suggestion message to the service.

We’ll see how long I keep this setup going.


This post originally appeared on the Linux Server Diary.