Wednesday, 29 July 2009

Django development workflow

Speak to 10 different Django developers and you will find 10 different ways of working. Speak to them the following year and you will no doubt find another 10 evolutions. This is just one of them, pickled in the moment.  I have a longish history with python but my Django experience is much more recent, but nonetheless I think this distills a some sound practices that I've learned from others, and I hope might be of help to others just starting out in Django.

Tools of the trade

OSX, Macports, Ubuntu Server, Vmware Fusion, Komodo IDE, Postgresql for my database, S3Hub, and Expandrive. Aesthetically I prefer textmate but code completion with tooltips, text completion, and mercurial integration are why I parted with more money and purchased Komodo IDE. Komodo is a little buggy at times but stability has improved since 5.1

Services

Amazon S3 for images, Ubuntu VSlices from MDhosting for (Australian) vm hosting, Bitbucket for repositories, Pingdom for website monitoring, and Canonical Landscape for vm monitoring. Landscape is more my way of giving back to Canonical for Ubuntu, because as a service in itself it is probably overpriced at this feature point.

Core Python packages

Django - tracking trunk of course. Virtualenv + wrapper, Pip, Mercurial for version control, and Fabric for deployment. The macport of Virtualenv + wrapper is recent but there is an issue with it adding 26 to the end of the link. Just need to rename the opt links. I Did work with Git for a while but find I get myself in less trouble with Mercurial especially when I need to revert. Maybe that's just me..

Django modules

South for migrations and Debug-toolbar are the two I can't live without. I would be curious if any people have others they absolutely can't go without installing in every project.

Project Structure

[domainname.com] > apacheconfs
                               > media
                               > logs
                               > wsgi_script
                               fabfile.py
                               > [projectname] > .hg
                                                         > apps > [internal_app_name]..
                                                         > project[s] > bin
                                                                           > templates
                                                                           manage.py
                                                                           > settings > __init__.py
                                                                                           (global settings)
                                                                                            dev_settings.py
                                                                                            staging_settings.py
                                                                                            prod_settings.py

                              

Daily Workflow

With a virtual env for each project with the projectname I create a postactivate script to cd into the manage.py directory with 'workon [projectname]'. Instead of running setup.py I usually symlink (I use a pylink script but I believe the virtualenv wrapper also does this) all external and internal apps into my virtual pythonpath unless for some reason it needs to explicitly be installed. I only put my own apps under my apps directory and even then I treat them as individual packages to be linked in - just to re-inforce in my head the concept of modular building blocks rather than one monolithic python package.

My internal Ubuntu Server staging vm is almost identical to production with only a few practical differences - the domain name and memory used by postgres, memcached etc. I pretty much follow the almanac for ubuntu setup of prod and staging. With Vmware fusion I fix the local vm's internal ip address, and alter the osx host file to point the staging domain to the vm.

At the project level, a Fabric fabfile defines setup() with all the tasks to setup the project on the server including creating the virtual env, cloning all the required repo's, and copying local files up to the server. The Fabric documentation examples don't appear to all work so I just define a setup_prod(), and setup_staging() which are decorated with the respective hosts and call the main setup(). My script looks something like this. I look forward to Fabric reaching 1.0, a tutorial being added to the docs, and the api stabilizing.

I use Mercurial hg update to switch between the default (trunk) branch and a dev branch. Most changes get made to the dev branch or if it's major surgery I'll clone the rep and create a new virtualenv. Fab deploy_staging pushes the dev branch changes to the staging server before I merge with default. Since the staging vm is for all intents and purposes identical to prod but with debug still switched on, there should be no nasty surprises when I merge with default and fab deploy_prod. The deploy script touches the djangowsgi.py script to effect the changes. There are additional fab functions for restarting apache, postgres, and updating any external packages as required.

Since the media files use version numbers in the filename I don't put media under version control and just use the nicely evolving S3Hub to organize my S3 media bucket, though I aim to include it in my fab script soon.

Development methodology

In terms of actual development I tend to work differently depending on the phase of the project. At the start I try and draw up the ultimate feature wishlist/architecture and then tear it down to the absolute minimum that is required to get the project into production and into alpha. In the same I break down my apps into simple modular components. I have seen too many monolithic projects fail because they spend too much time adding in requirements when really they should be removing. Pre 1.0 I cherry pick what tests I write. Tests become more important the closer you get to version 1.0 and start wanting to add all the other cool stuff in your wishlist. This is where you lose focus of the project as a whole and need the tests to make sure that while you're focusing on cool feature x you don't set fire to your house. I have whole projects I just can't bear revisiting because the testing foundations are pathetic. This is pretty much just agile development except I'm a little more pragmatic on test driven development pre 1.0.

References

There are a lot of good blog posts out there, but aside from the aforementioned Ubuntu Almanac I practically learnt Django and some sound 'its about the app stupid' practices from James Bennett's Practical Django Projects which is now infinitely more practical in it's 2nd edition. I'm also a big fan of Eric Holschers attempt to formalize conventions and evangelize testing. For some real project structures pinax and satchmo are nicely evolving projects which have already thought about this. Finally I couldn't live without the incredible effort that has gone into the main Django docs and django itself, which is making development a pleasure.

10 comments:

WorldMaker said...

"I would be curious if any people have others they absolutely can't go without installing in every project."

You mentioned South. Django-Registrations was on the list, but that is assuming its rightful place in django.contrib. OpenID support still requires 3rd party app(s) for now, though.

Django-Tagging is a must for me, partly because I have almost fallen into the "why not allow tagging on every model" pit.

Typogrify is handy.

Treebeard is slowly making its way into many of my projects. It makes tree structures in models a snap and provides a lot of powerful tools to work with them. It may even be surprising how common tree structures can be in database models. (I didn't realize how much I was doing the same tasks over and over again in subtly divergent ways until I started using Treebeard more.)

Tom said...

This is good, I like to read how other's structure their work.

Something I do is, below the templates folder I will have another folder named apps. Under this apps folder will be a folder per app in which contains templates for views specific for each app. It's really useful to have templates organised per app.

You may already do this but if not it might be of interest.

Jim Robert said...

I like the idea of making settings a full blown module rather than just a single file.

I've always just had half a dozen or so settings files that do a `from settings import *`, but I think your way is much cleaner and more intuitive.

good tips!

dudus said...

you mention you create a virtual environment for each project. I wonder if you mean virtual python instalations. If so I'd like to see how you configure your wsgi script and .htaccess apache conf.

Brett said...

@Tom - yep I missed that off but yes subdirectory for each app with templates.

@dudus - as it happens I only use virtualenvs in development since I generally don't have that many apps on the same prod/staging vm - so conflicting versions is less of an issue, but since I use modwsgi in daemon mode I don't think it would be that tricky to implement.

modwsgi VirtualEnvironments

rich said...

"At the project level, a Fabric fabfile defines setup() with all the tasks to setup the project on the server including creating the virtual env, cloning all the required repo's, and copying local files up to the server"

I'd really like to hear more detail on how you do this, if you have the time. Cheers

Brett said...

@rich I think the Fabric designers ultimately intend you to work differently to the way I have set mine up.
Their concept is that you would run fab deploy staging to get the staging settings applied to the deploy function where as I run fab deploy_staging to get the same effect with marginally more code. If I could make their way work I would use that..

Here's a pseudo version of my fabfile.py http://gist.github.com/158600.

rich said...

@brett
Thanks - I haven't really done anything with fab myself beyond spraying a farm of web servers using rsync.

It's great to have a fully working starting point to build from, which is so often what's missing in documentation.

Cheers
Rich

sidnei said...

You mention: "Landscape... as a service in itself it is probably overpriced at this feature point."

I am a developer in the Landscape project, and we are really interested in hearing any feedback that you might have, more specifically:

1. What features you would like to see that would make it worth it's price for you?
2. What features you find yourself not using at all?
3. What are you comparing Landscape against when you say it's overpriced?

There aren't many comparable services out there, and the ones we are aware of are much more pricey. We really want to make this a compelling product that it's worth its price.

Brett said...

@sidnei, the value of Landscape probably depends on the context.

As the number of instances under management increases so does the value of the service but in the small developer segment who is taking advantage of small cheap ubuntu vps and has a smallish number of vps's then the relative expense of Landscape is quite high. I would think for the small developer with 5 or less vps then a smaller (starter plan) per instance fee would be advantageous.

I'm more interested in SaaS monitoring than I am package management, so from that perspective adding in plugins for monitoring various applications in the ubuntu stack would add to the value proposition.

Hedged Down