March 16 2010 by Tobias McNulty
Django is a tool we use every day to build fantastic web apps here at Caktus, and a development sprint is a concerted, focused period of time in which developers meet in the same space to get things done on a project.
We're proud to annouce that Caktus is hosting another local Django development sprint in the Triangle (Raleigh, Durham, and Chapel Hill/Carrboro) area of North Carolina. The sprint will be held the weekend of March 20th and 21st in Carrboro Creative Coworking, and the purpose of this sprint will be to help push out bug fixes in preparation for the upcoming Django 1.2 release.
If you're interested in attending, no previous experience contributing to Django is necessary and the sprint will be a great opportunity to start. Work on other open source Django-based projects is welcome too. For more information, check out the corresponding wiki page.
We'll be there to open the doors at 9am both days. Courtesy of our sponsors there will be free drinks, snacks, and lunch to go around. Hope to see you there!
March 11 2010 by Tobias McNulty
Like just about everyone else, we've written our own suite of tools to help with building complex content management systems in Django here at Caktus. We reviewed a number of the existing CMSes out there, but in almost every case the navigation and page structure were so tightly coupled the system broke down when it came time to add additional, non-CMS pages.
We wrote a few little apps, django-pagelets, django-treenav, and django-crumbs, each of which manages different pieces of content (little snippets of content, full CMS pages, navigation, and breadcrumbs). All of the apps are available for free under an open source license on Google Code.
Decoupling was a great move for us, and the ability to plug and play any single part of the system is a huge benefit. Sometimes, however, the completely decoupled architecture was a bit of a pain: If we didn't provide a link from the pagelets app to the treenav app, how would it be possible to edit a page's corresponding navigation item on its change form in the Django admin interface?
Enter Generic Relations. Using Django's content types framework, it's possible to create admin inlines for generic relations with just a few simple lines of code.
In this case, I'll show how we allowed users to edit a page's corresponding navigation item in django-pagelets without requiring everyone (i.e., those who don't need it) to install django-treenav. First, define the generic inline in the admin.py file of the app that contains the model you want to link to:
from django.contrib.contenttypes import generic
class GenericMenuItemInline(generic.GenericStackedInline):
"""
Add this inline to your admin class to support editing related menu items
from that model's admin page.
"""
max_num = 1
model = treenav.MenuItem
Then, inside the Admin class for the related model in question, dynamically import and add GenericMenuItemInline to the admin's list of inlines based on whether or not it's in the project's INSTALLED_APPS:
from django.conf import settings
class PageAdmin(admin.ModelAdmin):
# ...
inlines = [MyOtherInline]
if 'treenav' in settings.INSTALLED_APPS:
from treenav.admin import GenericMenuItemInline
inlines.insert(0, GenericMenuItemInline)
For more information, see the corresponding pagelets admin.py and treenav admin.py. Thanks for reading and don't hesitate to post comments if you have any questions!
March 08 2010 by Colin Copeland
We're always looking for new tools to make our development environment more robust here at Caktus. We write a lot of tests to ensure proper functionality as new features land and bug fixes are added to our projects. The next step is to integrate with a continuous integration system to automate the process and regularly check that status of the build.
After attending Dr. C. Titus Brown's "Why not run all your tests all the time? A study of continuous integration systems." talk at Pycon and seeing Django's Hudson setup, I figured I'd take a look at Hudson CI.
Installing Hudson and basic setup
Hudson is very easy to setup. I started with a fresh Ubuntu 9.10 install on the smallest Rackspace cloud instance and had it running after a few commands. I followed the Debian setup instructions, which basically consists of:
$ wget -O - http://hudson-ci.org/debian/hudson-ci.org.key | sudo apt-key add -
$ echo "deb http://hudson-ci.org/debian binary/" >> /etc/apt/sources.list
$ apt-get update
$ aptitude install hudson
$ apt-get upgrade
That's it! It's already up and running on port 8080 using it's own web server. Go ahead and pull it up in your browser.
As a test, let's setup django-crm (a Caktus open-source community project) as our first Hudson job. Click "New Job", type in a job name, click "Build a free-style software project", and hit OK. django-crm contains a sample project that we'll use to run the test suite. On the job configuration page, check Subversion in the Source Code Management section and type in the Repository URL:

Click Save, run the job by clicking "Build Now", and check out the Console Output:
Started by user anonymous
Checking out a fresh workspace because /var/lib/hudson/jobs/django-crm/workspace/sample_project doesn't exist
Checking out http://django-crm.googlecode.com/svn/trunk/sample_project
A manage.py
A site_media
A site_media/css
A site_media/css/jquery.autocomplete.css
A site_media/css/django-contactinfo.css
A site_media/js
A site_media/js/jquery-ui-1.7.2.custom.min.js
A site_media/js/jquery-1.3.2.min.js
A site_media/js/django-crm.js
A site_media/js/jquery.autocomplete.min.js
...
Finished: SUCCESS
Cool, now let's run some tests. Too keep things simple, let's grab Django and a few dependencies using aptitude:
$ wget http://www.djangoproject.com/download/1.1.1/tarball/
$ tar xzvf Django-1.1.1.tar.gz
$ cd Django-1.1.1
$ sudo python setup.py install
$ aptitude install python-dev python-imaging python-setuptools python-pip
To run the tests, add an "Execute shell" build step in the Build section with this command:
#!/bin/bash -ex
cd sample_project
python manage.py test crm
Run the job again and look for the test results in the console output:
[workspace] $ /bin/sh -xe /tmp/hudson6670261053226891793.sh
+ cd sample_project
+ python manage.py test crm
...
Finished: SUCCESS
XML Test output
To integrate Hudson with the Django test suite, I used unittest-xml-reporting. Just "pip install unittest-xml-reporting" and add the following lines to your settings file:
TEST_RUNNER = 'xmlrunner.extra.djangotestrunner.run_tests'
TEST_OUTPUT_VERBOSE = True
TEST_OUTPUT_DESCRIPTIONS = True
TEST_OUTPUT_DIR = 'xmlrunner'
Then check "Publish JUnit test result report" in the Post-build Actions section and add the path to the test XML output "sample_project/xmlrunner/*.xml":

Run the job and you should see a new "Test Result" link in the navigation. Now you can view the test results right in your browser window.
Coverage
To add coverage reports, I used Ned Batchelder's coverage.py (pip install coverage). Navigate to Hudson's plugin manager (Hudson -> Manage Hudson -> Manage Plugins), install the Cobertura Plugin, and restart Hudson when prompted. Then modify your shell script like so:
#!/bin/bash -ex
cd sample_project
coverage run manage.py test crm
coverage xml --omit=/usr/
This will generate an XML coverage report in the working directory, so we just need to tell Hudson where to look for it. Check "Publish Cobertura Coverage Report" in the Post-build Actions section and enter the path to the report:

Run the build again and you should have access to a new "Coverage Report" link.
More to come...
This was just a simple example of getting Hudson setup with a Django project and I know a lot more can be done with Hudson (check out the large number of available plugins). The top items on my todo list are: see Hudson setup environments with virtualenv and pip, integrate more closely with the test suite (possibly using nose), check for PEP compliance, and setup build failure notifications. I hope to write more as I continue to setup our Hudson environment!
References
A few useful Hudson/Python/Django links I discovered while running through this setup: