Installing PostgreSQL 8.3 on Leopard

This is the documented path to my discovery of PostgreSQL 8.3, which I’ve never used before. Now that MySQL‘s community is getting hammered to death by Sun, and thanks to all the good things I’ve heard about it over the years (including enhanced performance on multicore systems and greater scalability), I really wanted to install it and play with it.

Frankly, it’s not easy. At all (actually this is why I think MySQL is so popular, because of the ease of installation!) So hang tight and read on. Continue reading

Quick spec from your Python tests

Using Python’s own unittest package, here’s a small script that can iterate over your test suite to output a small, quick, nice list of the tests in your application:

[source:python] import unittest

loader = unittest.TestLoader() tests = loader.loadTestsFromName(‘path.to.your.tests.package’) for test in tests.tests: print test._tests[0].class.name.replace(“Test”, “”) for method in test._tests: print ” %s” % method._testMethodName.replace(“test“, “”).capitalize().replace(“_”, ” “) [/source]

This would yield something like this: Business     Accounts have at least one entry     Clerks cannot close accounts Security     Users can create new accounts     Anonymous users cannot access private areas ...

Of course, you’ll get better results if you follow Google’s naming conventions for your tests… ;) This is not rspec (nor an alternative to it) but it might be useful to some of you.

Just as a reminder for Django users: you might need to

setenv DJANGO_SETTINGS_MODULE application.settings

or

export DJANGO_SETTINGS_MODULE=application.settings

in order to make the script work properly! At least I had to :)

Django Architecture Approaches

I’ve just had a very interesting conversation with my colleague Marco about different approaches to the organization of code inside a Django application.

As you might know (and if you don’t I’ll tell you anyway), Django’s views (somehow occupying the “Controller” level in an MVC architecture) must take (at least) an HttpRequest instance as a parameter and must return an HttpResponse instance. That’s how it goes in Django, this is the law. This means that you must be sure that the last instruction in your request processing code (in whichever way you’ve organized it) must return an HttpResponse instance, usually calling the HttpResponse() constructor (or of any of its useful subclasses), or by calling the django.shortcuts.render_to_response() function, or something similar.

This has, in my opinion, a major drawback: it might limit code reuse and it increases the coupling in the code. Everything’s not lost, however. Continue reading

REST + HTTP (Basic + Digest) Authentication support for Django’s test Client class

Django has a nice support for unit and functional testing; however, its django.test.client.Client class does not support PUT and DELETE requests, which might be useful if, like me, you’re doing some kind of REST implementation using that framework. There’s an open ticket about it, but for the time being, here’s my wrapper that supports those methods as well as GET and POST:

[source:python] from cStringIO import StringIO from django.test.client import Client as DjangoClient, encode_multipart from django.utils.http import urlencode import base64 import md5

class Client(DjangoClient): “”" Wrapper and drop-in replacement around Django’s own test “Client” class, providing PUT, DELETE and OPTIONS support, as well as HTTP Basic + Digest Authentication support. NOTE: the django.test.client.Client does not directly support PUT, DELETE or OPTIONS requests so we’re using the “request()” method directly… there’s an open ticket about it: http://code.djangoproject.com/ticket/5888 “”"

auth = { }
def http_basic_login(self, username, password):
    base64string = base64.encodestring('%s:%s' % (username, password))[:-1]
    self.auth = { "HTTP_AUTHORIZATION": "Basic %s" % base64string }
def http_digest_login(self, method, url, params, response, username, password):
    (authmeth, auth) = response['WWW-Authenticate'].split(" ", 1)
    if authmeth.lower() != 'digest':
        return
    amap = {}
    for itm in auth.split(", "):
        (k, v) = [s.strip() for s in itm.split("=", 1)]
        amap[k] = v.replace('"', '')
    try:
        realm    = amap['realm']
        qop      = amap.get('qop', '')
        nonce    = amap['nonce']
        opaque   = amap['opaque']
    except:
        return
    cnonce = "01b6730aae57c007"
    nc = "00000001"
    query_string = "&".join(["=".join(item) for item in zip(params.keys(), params.values())])
    uri = url + "?" + query_string
    ha1 = md5.md5('%s:%s:%s' % (username, realm, password)).hexdigest()
    ha2 = md5.md5('%s:%s' % (method, uri)).hexdigest()
    if qop:
        chk = "%s:%s:%s:%s:%s:%s" % (ha1, nonce, nc, cnonce, qop, ha2)
    else:
        chk = "%s:%s:%s" % (ha1, nonce, ha2)
    response = md5.md5(chk).hexdigest()
    self.auth = {
        "HTTP_AUTHORIZATION": 'Digest username="%s", realm="%s", nonce="%s", uri="%s", response="%s", opaque="%s", qop=auth, nc=%s, cnonce="%s"' % (username, realm, nonce, uri, response, opaque, nc, cnonce),
    }
def http_logout(self):
    self.auth = {}
def pre_request(self, url, data):
    r = {
        'CONTENT_LENGTH':  None,
        'CONTENT_TYPE':    'text/html; charset=utf-8',
        "HTTP_USER_AGENT": "Django Unit Test HTTP Client",
        'PATH_INFO':       url,
        'QUERY_STRING':    urlencode(data, doseq=True),
    }
    r.update(self.auth)
    return r
def get(self, url, data, **extra):
    r = {}
    r.update(extra)
    r.update(self.auth)
    return DjangoClient.get(self, url, data, **r)
def post(self, url, data, **extra):
    r = {}
    r.update(extra)
    r.update(self.auth)
    return DjangoClient.post(self, url, data, **r)
def delete(self, url, data):
    r = self.pre_request(url, data)
    r["REQUEST_METHOD"] = "DELETE"
    return self.request(**r)
def options(self, url, data):
    r = self.pre_request(url, data)
    r["REQUEST_METHOD"] = "OPTIONS"
    return self.request(**r)
def put(self, url, data, form):
    BOUNDARY = 'BoUnDaRyStRiNg'
    MULTIPART_CONTENT = 'multipart/form-data; boundary=%s' % BOUNDARY
    encoded = encode_multipart(BOUNDARY, form)
    r = self.pre_request(url, data)
    r.update({
        'CONTENT_LENGTH': len(encoded),
        'CONTENT_TYPE':   MULTIPART_CONTENT,
        'REQUEST_METHOD': 'PUT',
        'wsgi.input':     StringIO(encoded),
    })
    return self.request(**r)
def hello(self, url, data, **extra):
    """Sends a fake 'HELLO' request which returns a 405 answer :)"""
    r = self.pre_request(url, data)
    r["REQUEST_METHOD"] = "HELLO"
    r.update(extra)
    return self.request(**r)

[/source]

Hope it helps! Of course you could extend this to support OPTIONS, HEAD or other HTTP methods you could find in the specification.

Update, 2008-03-05: Following Yoan’s comment below, I’ve DRYed the code a bit. Neat.

Update, 2008-03-11: I’ve added HTTP Basic Authentication support to the class (and changed the post title accordingly).

Update, 2008-03-13: Another modification: now the client supports HTTP Digest Authentication (Yay! To use it, make a first call to your server and then pass the response as a parameter to the http_digest_login method), plus support for the OPTIONS verb, plus another method which sends a method with a “HELLO” verb, which of course does not exist… and which will (normally) return a 405 response!

Archimedes Reloaded

Give me an API and I’ll move the web.

OK, I’m actually building the API myself this time. That’s why so few posts this month. RESTful, featuring multiple export formats, secure, configurable, usable on top of any existing Django application. It can even generate its own wrappers in a myriad of programming languages.

Install MySQL_python in Leopard

I wanted to make my Django blog engine work on Leopard using MySQL as a database engine. I had a hard time making it work, partially because of my lack of knowledge of Python, partially because I am using MAMP instead of a “/usr/local/mysql”-like MySQL installation, partially because of Leopard itself.

The problem is, basically, that if you try to do the “easy_install MySQL_python” thing, it won’t work in Leopard (the compilation of the native code fails). Here’s how I made it work, following the instructions in this post in the MySQL forums, and doing some tweaking manually. Continue reading

My first Django project

So here it is, my first Django project: the gazillionth blog engine on the planet!. As if there weren’t enough, right? :) Actually it was a practical and easy way to learn the Django project, and the result is pretty neat. Feel free to download it, play with it, and give me your feedback. Here’s a sample screenshot in Safari:

screenshot.png

Creating this project I have had a practical experience comparing both Django and, of course, Rails. The subject is not new in this blog; however, this time I could play with both frameworks and as such, I can bring my small amount of confusion in this big framework tar pit. Continue reading

Starting with Django in Leopard

As I’ve written earlier, I’m playing with Django these days. It’s a refreshing change, I must say, even if I admit that I prefer Ruby’s syntax to Python’s. Of course that’s just a purely subjective impression (I’m writing this while I try to avoid the rotten tomatoes thrown by angry pythonistas reading this) that does nothing to do with the power of the Python language + framework, which is by all means absolutely impressive.

In any case, I had to install a working Django environment in my machine, and while following the excellent and free Django Book instructions, this is what I did in my Leopard installation to have it up and running (Leopard already comes bundled with Subversion 1.4.4, Python 2.5.1 and SQLite 3.4.0, so you don’t need to do anything about them): Continue reading

Django & Leopard & the UTF-8 error

If you use Django on Leopard, you might encounter a strange “Locale UTF-8 not found” error when running your application. This is due to a bug in Terminal.app, albeit an easy to fix one: just go to the Preferences pane / “Settings” page / “Advanced” tab and uncheck the “Set LANG environment variable on startup” checkbox. Reopen your Terminal session, load the Django application and voilà! Your bug has disappeared. The screenshot below might help too:

solution.png

Hope this helps!

PS: yes, I’m doing Django and hence Python these days ;)

Rails vs. Django

While watching the Google TechTalk about Django, I came accross this interesting paper highlighting differences between Rails and Django, which might be interesting to many of us in the field.

For those interested, Django is a powerful, MVC framework for creating web applications using the Python programming language. It is very similar in some respects to Rails, but some consider Django to be better suited for high-traffic websites. I haven’t used Django extensively (nor Python as a matter of fact, besides the typical “Hello World” thingy) so I won’t say anything else but the fact that I prefer Ruby‘s syntax to Python’s.

Of course this is a subjective opinion! In any case, if it’s anything like Rails, it’s worth considering. Definitely. Dynamic languages are the way to go.

Update, 2008-01-11: You might want to check out this other article of mine!