Intro to Django — Part 2

Django 1.3 edition

Steven F. Lott


Code Kata

Review Model

View, Dynamic Content

Next Parts: Presentation, Admin Pages, etc.

Code Kata

Simple Inventory of ingredients, recipes and locations.


Use Case



Model Code

from django.db import models

class Ingredient( models.Model ):
    name = models.CharField( max_length=64 )
    safety = models.CharField( max_length=64 )

UNITS = ( ("ea.","Each"), ("lb.","Pounds"), ("qt.","Quarts") )

class OnHand( models.Model ):
    ingredient = models.ForeignKey( Ingredient )
    count = models.FloatField( )
    units = models.CharField( max_length=64, choices=UNITS )


Several standard pieces to a handling a request.

Common Aspects:

All those interlocking pieces and parts... we'll do this in steps.

urls(request) → view function

Much fancy URL parsing available to locate the view function.

A module creates a well-known global variable.

Any code can create the urlpatterns variable. Keep it simple.

Should be structured.


Can enable the built-in admin. We'll get to that.

For now we'll add two parsing rules.

url(r'^$', 'tutorial.inventory.views.home', name='home'),
url(r'^inventory/', include('tutorial.inventory.urls') ),

The empty path (r'^$') will use the home function in views module.

The r'^inventory/' includes another urls module.


from django.conf.urls.defaults import patterns, include, url

urlpatterns = patterns('tutorial.inventory.views',
    url(r'^$', 'home' ),
    #url(r'^ingredient/$', 'ingredient' ),
    #url(r'^location/$', 'location' ),
    #url(r'^onhand/$', 'on_hand' ),

Top-level used include(); Using parsing already done in parent.

Cool URIs don't change

view( request ) → response

The Django ORM to get data.

We have all of Python to process data.

HTTP Response object



from django.http import HttpResponse

def home( request ):
    return HttpResponse( "Hello from {0}".format(
    content_type='text/plain' )

Always write something like this first just to help debug your Apache and mod_wsgi configuration.


Handy developer server.

./ runserver

Runs a Django service on

HTTP Request → url pattern match → view function → HTTP Response.


Some folks get to the ./ runserver command and think they're done.


You need Apache (and a cache of some kind) to handle real requests.

Why? User's Browser is slow.

Handling the (1) download drag and (2) secure port 80 is Apache's job.

Unit Testing

Django Unit Test framework has a special self.client.

This can be used to send GET and POST requests to the web server.

Numerous assertions available to check responses


Provide good CSS class and ID to help unit testing.

<span class="query">Lime</span>

Can make unit testing much simpler.

Testing a View Function:

from django.test import TestCase
from tutorial.inventory.models import Ingredient, Location, OnHand

class Test_Ingredient_View( TestCase ):
    fixtures = [ "sample_data.json" ]
    def test_should_get_all( self ):
        response= self.client.get( "/inventory/ingredient/" )
        self.assertContains( response, "<td>lime</td>", status_code=200, )
        self.assertTemplateUsed( response, "ingredients.html" )
    def test_should_get_specific( self ):
        response= self.client.get( "/inventory/ingredient/lime/" )
        self.assertContains( response, "<span class="query">Lime</span>",
            status_code=200, )
        self.assertContains( response, "<td>lime</td>", )
        self.assertTemplateUsed( response, "ingredients.html" )

TDD Sidebar

Tests are so easy to write.

Write them first.

The /inventory/ingredient/name/ is going to break when someone uses "rum/dark" and "rum/light" as ingredients.

We should

Dynamic Content

Adding features to parse the URL.

  1. More rules in
  2. More functions in
  3. Also, this may lead to additional templates


from django.conf.urls.defaults import patterns, include, url

urlpatterns = patterns('tutorial.inventory.views',
    url(r'^$', 'home' ),
    url(r'^ingredient/(?P<name>.*?)/$', 'ingredient' ),
    url(r'^ingredient/$', 'ingredient' ),
    #url(r'^location/$', 'location' ),
    #url(r'^onhand/$', 'on_hand' ),

Collect part of the path in name; provide it to the view function.


from django.shortcuts import render
from tutorial.inventory.models import Ingredient

def ingredient( request, name=None ):
    if name:
        objects = Ingredient.objects.filter(
            name__icontains=name )
        objects = Ingredient.objects.all()
    return render( request, "ingredients.html",
        {'objects':objects, 'name':name}


The objects attribute of a Model class has a number of functions.

And there's a LOT more capability.

GET and POST requests

The request.GET has the parsed data from after the ?.

The request.POST has parsed parameters in the content.

The request.FILES has file-like objects for any attached files.

Plus the user, cookies, and session information.


Three parts:


We're not even close to done with the Django feature set.