Up to Speed

June 15, 2008. Filed under django 72

(This is part two in Ken's series, Wielding Django. - Will)

Last time, we built up a minimal Django app just to show the core ideas. In this article, we'll flesh out some of that minimalism and make our first (if still trivial) web application.

settings.py

For just a few parameters, calling settings.configure() with each is fine. But as we start to use more of Django, we'll want to set more parameters, which can get unwieldy. So Django lets you put those settings in a separate file. Most people call it settings.py, but you can call it whatever you want.

Starting with the project directory you used last time, put this in settings.py:

qaodmasdkwaspemas0ajkqlsmdqpakldnzsdfls

It's also about time you made this app be a proper Python module. First, make sure that the name isn't taken; tempting ones like django, site, and test are spoken for already. To be safe, if your directory is named mydir, run import mydir at a Python prompt; it should fail. Once you have a good package (directory) name; just make an empty file called __init__.py:

qaodmasdkwaspemas1ajkqlsmdqpakldnzsdfls

manage.py

Before you go typing in that code from last time to start the server again, note that Django has a flexible framework for handling all sorts of managerial tasks. Most people tap into it using a file called manage.py, which you should copy into your project directory:

qaodmasdkwaspemas2ajkqlsmdqpakldnzsdfls

The core of that file is just:

qaodmasdkwaspemas3ajkqlsmdqpakldnzsdfls

The project_template directory contains a few other things too, which django-admin.py createproject dumps in every new project. You don't need them; we're building up just what we need.

Then you can run:

qaodmasdkwaspemas4ajkqlsmdqpakldnzsdfls

By default, the server only gets connections from the local machine. If you want to show it off to someone else, run instead:

qaodmasdkwaspemas5ajkqlsmdqpakldnzsdfls

(or whatever port you want), then its URL will be http://your-machine:8000/.

The management commands look for settings in settings.py by default.

Request parameters and AJAX

Let's get back to the app. A static page is rather boring; let's go straight to AJAX. Here's a new app, math_app.py:

qaodmasdkwaspemas6ajkqlsmdqpakldnzsdfls

Change ROOT_URLCONF to math_app, start the server, and go to http://127.0.0.1:8000/compute/add/?a=2&b=4. You should see 6.0.

The basic structure should be familiar: urlpatterns configures how URLs map to views, and a view (compute) constructs a response from a request. But a few things are changed:

  1. The regex now matches a parameter: \w matches an alphanumeric character, \w+ matches a sequence of 1 or more such characters, and (?P<op>\w+) gives that sequence the name op. This is all ordinary Python re stuff.

  2. Django passes parameters extracted from the regex to the view: compute gets an extra keyword argument: op. It's always a string, even if the regex pattern matches numbers (\d).

  3. request.GET is a dict with the GET parameters (a and b), again always strings.

And getattr(operator, 'add') is the same as operator.add. Here's an alternative implementation of that part:

qaodmasdkwaspemas7ajkqlsmdqpakldnzsdfls

Handling errors

Okay, so you made a typo and got an error page. (If you didn't, go add an assert False to the view function to make it fail.) It's not very helpful right now, for two reasons. We'll fix both real quick.

  1. When there's an error, Django goes looking for a variable handler500 in the ROOT_URLCONF module. Oops, we don't have one. We can just import it, though (along with handler404, which handles page-not-found errors):

qaodmasdkwaspemas8ajkqlsmdqpakldnzsdfls

Those are just views; you can provide your own too. Also, most people just import *, but that makes
PyFlakes not work. btw, 404 and 500 are standard HTTP status codes.

  1. When you're developing (but not in production!) it's helpful to see the details of the error. Django provides a really nice error page for server errors if you're in DEBUG mode. Just add this to settings.py:

qaodmasdkwaspemas9ajkqlsmdqpakldnzsdfls

Now make that typo again, and hit the error page. Click on the code lines and "Local vars" headers to expand them. Enjoy! Now let's get back to work.

Serving static pages

Okay, so seeing a number come back from a server is kinda boring. If we want user interface, we need some HTML. You could of course just make another view function that returned an HttpResponse with a all the HTML passed in a big string (just pass mimetype='text/html' as a parameter, so the browser doesn't think it's text). But it's a pain to maintain that. Fortunately, Django provides a better way: the django.views.static.serve built-in view. It takes two parameters: document_root and path. Here are two ways you can use it:

qaodmasdkwaspemas10ajkqlsmdqpakldnzsdfls

(Don't forget to pass request to the other view function; I almost did!) Or:

qaodmasdkwaspemas11ajkqlsmdqpakldnzsdfls

The dict as the third parameter of url is a way to pass extra parameters to the view. Also, you'll notice that you can specify views by a string; Django just imports them and uses them. You can use both tricks for your own views as well.

Notice an important difference between this approach and what you're probably used to from PHP or most other web development: there's no necessary correspondence between URLs and files. You are in complete control of that mapping. For example, you might be used to '/' being just a synonym for '/index.html' (or index.php or whatever). But on your Django server, /index.html gives you an error (try it!), because you didn't explicitly state that mapping.

Anyway, let's make a user interface. Make a static directory in your app and put this HTML in static/index.html:

qaodmasdkwaspemas12ajkqlsmdqpakldnzsdfls

I used Google's AJAX API hosting to get Dojo; thanks to Will for pointing out that useful service.

That's really ugly, of course, but it shows the idea. Join us next time as we start making a real app, using some simple techniques that may be new to even experienced Django people.