excess.org

Ian Ward

Consulting
Boxkite Inc.
Software
CKAN contributor/tech lead
PyRF primary contributor
Urwid author
Speedometer author

Presentations
Contributing to Open Source
IASA E-Summit, 2014-05-16
Urwid Applications
2012-11-14
Urwid Intro
2012-01-22
Unfortunate Python
2011-12-19
Django 1.1
2009-05-16

Writing
Moving to Python 3
2011-02-17
Article Tags

Home

Ian Ward's email:
first name at this domain

wardi on OFTC, freenode and github

Locations of visitors to this page

Django 1.1 Talk Text

Django 1.1 Layers
Posted on 2009-05-16, last modified 2009-05-19.

This is the text from the Django 1.1 talk I gave on Friday May 15 at Algonquin College for FOSSLC's Geocamp/Summercamp 2009.

I have tried to format this in a way that is well suited to skimming and easier to access from the web than reading the original slides. If you find this useful, please let me know.


Django 1.1

If you haven't taken a moment to look around this web site, my name is Ian Ward and I am an independent consultant here in Ottawa.

I work mostly with Linux, embedded systems and open source technologies, and when I am building a new web site I use Django.

My hope is that this talk will convince you to seriously consider Django for your next web development project.


Contents

I will be covering a number of topics. If anything is unclear or if you notice something I need to correct, please send me an email. I will post answers to questions on this blog.

There will be a couple of programming themes in my talk. The first is the don't-repeat-yourself principle. If you've written something once, you shouldn't have to write it again. The second is that the correct approach should always be the easiest approach.


return to Contents

Python Programming Language

Let me start by talking about the language Django is written in. Django is a Python Framework, not a content management system, so you will be using Python to build Django web sites. That's a good thing, and these are some of the reasons why.

Python is a dynamic language that is easy to read and work with. Including when you're working with other people's code.

Python is written in a procedural style, and has classes and objects when you need them, so it will be familiar to C++ and Java programmers.

However, it has dynamic types means you write less code and it's much faster to develop in than C++, Java. Also, It's a scripting language so there's no compiler to wait for.

People run it everywhere from embedded systems to huge server clusters.

Included modules that do almost everything you could ever need. When you need help the documentation is great, and there is a large, active community to help as well.


Python a Controversial Choice?

When I started using Python almost 10 years ago, it was difficult to sell people on it, especially for companies with investments in other languages. Some organizations may still have resistance to adding another language to the ones they already use.

But when even two of the highest profile technology rivals who rarely see eye to eye on anything both decide to work on and promote Python it suggests that there is something good there.

Companies are coming to the realization that they already have Python code, perhaps as a result of some of an eager programmer looking to make his or her work easier. Or perhaps there is a piece of software adopted by the company that needs to be extended or customized.

These days Python has become more and more accepted.


Python Code

print("Hello World")

def print_odd_numbers(numbers):
    for num in numbers:
        if num % 2:
            print("%d is odd" % num)
print_odd_numbers([5, 32, 98, 365, 422, 913])

import os
for var_name in os.environ:
    if "LANG" in var_name:
        print(var_name + "=" + os.environ[var_name])

Here is some simple Python code for the benefit of those that haven't yet picked up Python.

We have “hello world”, of course.

Next is a function that prints out the odd values passed in.

Finally we get the environment variables from the “os” module, which are in a dictionary (also called a hash in Perl and other languages) and print out the name and value of the ones that have “lang” somewhere in the name.

Python doesn't force you to use semicolons and has no closing braces or tags, it uses indenting to determine which statements belong in which blocks. This means that your indenting has to be correct, so your code will be easier for other people to read and understand.

Some people are still not sold on the idea of having significant whitespace, but all it really means is that everyone writing code must have configured their editors properly.


Python Steals Good Ideas

Python has been quite willing to take good ideas from other languages.

For example, Python takes the best aspects of Java's Unicode strings. You declare your source file encoding to create static Unicode strings in the source, and you can easily decode and encode between Unicode and binary strings.

List Comprehensions come from Haskell. They're a tidy way of applying filters and transformations on lists. I find them much easier to understand than functional style programming with map and lambda.

And generators are taken from high level languages like Icon. They let you suspend and resume execution of functions that generate values and need to maintain their own state.

Here the state being maintained is the current and previous Fibonacci number, nicely encapsulated as local variables. This way there's no need for complicated classes and objects when all you are doing is generating values or simple data processing.


Python Everywhere

Python runs almost anywhere. It runs on all the major operating systems, and some minor ones. It runs on Palm Handhelds and Nokia phones.

I think someone once said that all programs expand until they have their own scripting language. Well, you can save yourself some time and make your users happy by giving them a complete language.

You can embed Python inside your C/C++ programs to provide a scripting interface. You can also extend Python with modules written in C, for anything performance critical or to make use of libraries internal to your company.

There are number of different implementations of the Python interpreter:

Iron Python from Microsoft lets you run Python on .net, with access to all the .net facilities and libraries.

If you have a Java infrastructure you can use Jython to compile your code for the JVM. This is great for Django because you can hand your Web administrator a WAR file containing your Django project and they never need to know its not written in Java. They just plug it in and start it up.

PyPy is a Python interpreter written in Python itself, and Unladen Swallow is a fork of the standard Python interpreter. Both are working on just in time compilation and other techniques to try to make Python code perform almost as well as C code.


Python Standard Modules

I mentioned that you can extend Python to work with your own internal libraries. If you want to use a standard library, there is likely already a module written for you

This is a partial list of the standard modules included with Python

Everything you are likely to need is already included and all the standard modules are well tested and fully documented.

Python people call this “Batteries Included” and these modules can save you a huge amount of programming time.

If you need something that is not included, there are thousands of third party modules also available in the Python Package Index available from the main site at python.org.

Let me end this introduction to Python by paraphrasing something one of the Django developers said at a talk he gave a couple months ago. Django was written because the developers needed a web framework and they loved to work in Python. So even if you decide not to use Django, please use Python, it is a great language.


return to Contents

Django Overview [1]

Now on to the main topic!

There are some web frameworks that cobble together components from lots of sources, so called “best of breed” frameworks. Django is not one of those. “Full-stack” means that everything in Django is designed to fit well together, be documented and tested together. This is good for changes that cross components in the framework and has allowed Django core features to progress rapidly

The drawback of course is that innovation does happen elsewhere, so Django might not have every feature of some other tool you are used to. This is accommodated somewhat by the fact that Django's layers are separate so you can use your favorite object relational mapper or template library if you choose to.

Django is released for free under a BSD license which means there are essentially no restrictions on what you can do with it. You have all the source code. If you want to modify it, package it with a device and your own code to sell, you're free to do that. It would be rare that you need to change Django itself, but if you've fixed a bug or added a feature they would appreciate it if you send your code to them, but there is no obligation to do so.

I just spoke about some of the benefits of working with the Python language.


Django Overview [2]

Django does not aspire to some programmer's vision of design purity. It was built to serve a quickly changing local news website, a decidedly non-technical environment with lots of real-world quality and time pressures.

Nothing is added to Django unless it serves a useful purpose. Allowing quick and correct changes to web sites is the focus of development.

There are a number of best practices for web development that are built into Django, so it makes it easy to do the right thing all the time. Safe handling of user data, separation of presentation from business logic and clean URL namespaces should be part of your project from the beginning, not features to be tacked on later.

Certainly here in Canada, Supporting English and French is a must, and Django is designed for multiple languages. Which language is displayed is user configurable, but by default based on what the user has selected in their browser as their preferred language.

If you're building a Government of Canada web sites that require that standard empty “English or French” front page of course you can do that too.


Django Layers

Django is described as a Model Template View or MTV framework, which is similar to a Model View Controller design, but modified slightly to better represent the structure of web applications.

The concepts are similar enough, so if you're used to MVC you should feel right at home with MTV.

The View layer includes business logic and request handling. Templates determine the appearance of the website. The Model and object relational mapping layer defines data and relationships that are stored in the database.

I will speak in more detail about each of these layers below.


Django Internationalization

Django has been localized into 30 different languages, with 18 more at about 70 percent completion.

Django provides interfaces to the gettext library for translating strings, including ways to wait to translate strings until the very last moment. This is necessary for web applications because you don't know what locale a user prefers until you are about to render the response to their web request.

Django models, templates and view code supports internationalization by marking strings that need to be translated. There is a makemessages tool that will pull all these strings out to be localized.

There is no built-in support for internationalization in the database, however, so applications need to be built to store localizations of their own database text.


Django Database Support

Django supports four different databases for storing its model data.

Django lets you move data between databases with a dumpdata command that dumps your data into a database independent format, either XML or JavaScript object notation (JSON).

Easily moving data means programmers can pull data from a Postgres or Oracle database onto an SQLite database on their laptops and be able to work completely disconnected.

You can also use these XML or JSON dumps as fixtures that you can distribute with your applications.

dumpdata can nicely format its output with indenting and newlines. I regularly use this feature to track changes to certain applications' data in my database with a source control system.

If you have an existing database you want to use you can use the inspectdb command and Django will do its best to automatically generate models based on the tables it finds.


Django Scalability

So, Can Django run a busy web site? Yes, it can.

It stays running as part of the apache web server process with mod_python, or as a pool separate processes with mod_wsgi. This lets it quickly serve requests. mod_wsgi can be run as separate server accounts each with their own access restrictions, which is great for running multiple sites without them being able to affect each other.

Django supports internal caching of objects and pages with memcached or any other data store.

There is no global state except what is in the database so you can add more web servers to the pool when you need to handle more web traffic.

The biggest gains in performance come from serving static content from a separate web server, and adding external caching with a reverse proxy in front of your Django server.

Django is fast, but a slow client could tie up a Django process, so the reverse proxy will take that load and let you serve more users.

The busiest Django site right now is washingtonpost.com. It is about the 400th busiest web site online according to Alexa. There aren't any Django sites the size of Facebook yet, but Washington Post is proof that you can use Django to run a busy site.


return to Contents

Projects and Applications

Django is designed to encourage modularity and code reuse. Much of the functionality built in to other frameworks comes as applications in Django. This lets you choose which features you need, and extend or replace the provided applications as necessary.

Applications can interact in three main ways.

They can act as what Django calls middleware, which can intercept and modify requests and responses before/after they are handled by view code. Note that this is different that the common definition of middleware.

Applications can send and listen for signals, including signals sent before and after any data is saved to the database. Messages that need to be sent between applications can also be handled this way.

If one application depends on another it can also extend that application's models to build functionality on top.


Admin Application [1]

Some people come to Django just for its built-in admin application. Since it was introduced 5 years ago, I'm not aware of anyone that has been able to copy it in another framework.

For many simple sites the admin application may represent half the code you would have had to write if you used another framework.

It introspects your models and provides an interface for adding, editing and removing any of them. It can understand relationships and it can embed forms for creating models in a one-to-many relationship all in one page.

Aggregate actions are new in Django 1.1, you can define actions that can be applied from the list view. Fields can also now be editable in this view.


Admin Application [2]

This is the view for editing a single model. Relationships can appear as simple drop downs or with multiple sub-forms embedded in the same screen. There are shortcuts for creating related models, and for jumping to the web site to see what your changes look like.

The admin application can present different interfaces to people with different levels of access, and you can instantiate it multiple times with different layouts in each.


Admin Details

The admin site is really just another Django application. It uses Django's forms, formsets and generic views, all of which can be used in applications and interfaces you build.

It can be extended with your own functions as necessary. You can include your own custom form widgets and aggregate functions for your data.

It is not intended to be used as the main interface to your web site. It is intended for users that are creating and maintaining site content.


Databrowse Application

Related to the Admin application is Databrowse. Databrowse presents a read-only browsable interface to your models. If all you are doing is publishing structured information and don't need a custom interface, you can just create your models and publish the site.

Databrowse is also great for existing databases. You can generate models by introspecting an existing database with the inspectdb command , then use databrowse to provide an interface to your data.

This is great if you just want a simple web interface for viewing your data.


GeoDjango - Gis Application


www.marinemap.org

GeoDjango used to be a separate project but is now part of Django 1.1 as the gis application. GeoDjango works best with Postgis, the geographic extensions to the Postgres database.

This application extends Django's models to be able to run geographic queries, like returning all the points and polygons within a certain region, and calculating intersections.

You can import geographic data from projects like OpenStreetMap and with the OpenLayers JavaScript based slippy-map you can create your own mapping web site.

The JavaScript slippy-map can also be used in the admin application for interactively editing shapes and points.


Session and Auth Applications

The Session and Auth applications work by using middleware that intercepts the requests before the view code handles them.

The Session middleware identifies connections that it has seen before, and attached the corresponding session object to the request.

The Auth middleware uses the session object to store user login status. If the user is logged in it attaches the user object to the request as well.

The view code can now be much simpler because the auth and session information are just attached to requests that come in.

This is of course similar to how other frameworks work, but what's nice here is that you could replace the session or auth handling with your own middleware and the view code wouldn't have to be changed at all.


More About Auth

The Auth application comes with a model for users, of course, but it also has user per-model view/change/create permission settings. Permissions may be assigned to groups and users may belong to groups, inheriting the group permissions.

The Admin application uses these permissions to restrict what users with admin rights can do with the admin site, but you can use them and add new permissions for whatever your project needs.

There is also a simple message model for simple notifications like “saved successfully” after submitting data.

If you already have user authentication within apache or by another method, the Auth app can be configured to use it instead. This bypasses the passwords stored in the user model.


Flatpages and Redirect Applications

The Flatpages and redirect applications are examples of middleware that operate on responses that are returned from the view code.

The Redirect application has a simple table of old URLs and new URLs. If a 404 is returned from below and it matches one of the old URLs, Redirect will create a new response to redirect the user's browser to the new URL.

Flatpages is a table for static HTML pages stored in the database. If a 404 is returned from below and the URL matches one in the flatpages table, the middleware will create a new response with the static html, formatted with the usual site templates.

Flatpages are great for the “about us” or “frequently asked questions”-type pages on any web site. They change infrequently, but when you need to change them their content is there in the database so it's easy to do.


More Applications Included

There are a number of other applications included, they can all be useful and take care of features you would commonly require.

I will mention Cross Site Request Forgery protection. CSRF is a big security issue these days, and you can be a target thanks to insecure code some other web site. The CSRF protection in django adds hidden fields to your forms to verify that a request actually came from your site. This is really nice, Django already protects you from unsafe behaviour like letting users insert JavaScript in comments and other places, but it can also protect you from sites that don't have that protection.


return to Contents

Model Layer and ORM

Now on to how you actually build a Django application. First you start with models.

The model layer and Django's object relational mapper, or ORM, sit on top of your database and give you a nice clean Python interface to your data.

Models are built from Python classes where objects map to rows and members to columns. You can walk relationships forward and backward from the objects, and you can easily create queries that are run by the database on all of a model's objects or a combination of related models.

The ORM query language is very flexible, and it supports aggregate functions and complicated nested queries.

But, if you need to run your own SQL, and don't mind sacrificing database independence, you can do that too. Also, if you need better performance when pulling in lots of data from many rows you can request only the raw values from the database instead of having Django create an object for each row.

So you really have the best of both worlds: Safety and convenience by default and hand-tuned performance when you need it.


models.py

from django.db import models

class Musician(models.Model):
    name = models.CharField(max_length=50, unique=True)
    instrument = models.CharField(max_length=100)
    bio = models.TextField(blank=True)

class Album(models.Model):
    artist = models.ForeignKey(Musician, related_name="albums")
    name = models.CharField(max_length=100)
    release_date = models.DateField()
    num_stars = models.IntegerField()

This is an example of some models almost straight from the tutorial. Each field type corresponds to a database type and Classes correspond to database tables. Django can use this file to create the database tables to store these objects, so you are never forced to write SQL.

Reading and writing model data is safely translated to SQL for your database. You don't have to do any escaping or special handling of your model data.

If you don't specify one of the fields as the primary key, an auto-incrementing id field will be added for you.

The ForeignKey field creates a relationship between these two models, and the related name “album” will appear in the musician model as a way of accessing the set of albums by a musician. Notice that we don't need to repeat the relationship in the Musician model for this to work it is fully described in one place.

That feature also lets us build relationships to models that are not part of our own code or application, which is convenient at times.


Querying Models

from myproject.myapp.models import Musician, Album

Musician.objects.get(name="Chet Baker")
Musician.objects.filter(name__startswith="Ch", instrument="Trumpet")
Musician.objects.filter(albums__num_stars__gte=3)
Album.objects.order_by("name")[:10]
Album.objects.filter(artist__name="Ella Fitzgerald").update(num_stars=5)

from django.db.models import Avg, Max, Count

Albums.objects.aggregate(avg_rating=Avg("num_stars"))
Musician.objects.annotate(num_albums=Count("albums"), 
    best_rating=Max("albums__num_stars"))

Here are some examples of queries on the database.

get() will return a single instance from the database.

filter() returns a query set that you can iterate over like a list.

You can specify conditions that cross relationships and do inexact comparisons with double underscores, here startswith will just match the beginning of a value and gte will match values greater than or equal. Here we will have a queryset with all the musicians having albums rated three stars or more.

order_by() applies an ordering to a queryset, this is the regular Python slice notation here that is used to retrieve the first 10 albums returned.

Query sets are not just regular lists, you can apply changes to them with update(). Here we are setting giving all Ella Fitzgerald's albums 5 stars.

aggregate() and annotate() are new in Django 1.1. aggregate() will return a dictionary with just the values specified. annotate() will attach a computed value to each of the objects in a query set.


return to Contents

View Layer

Django's View layer encompasses middleware, URL mappings, view code, forms and file upload processing.

MVC frameworks would call this the Controller. Django's view layer is responsible for determining what data is displayed at each URL, and handling changes to that data.


urls.py

from django.conf.urls.defaults import *

urlpatterns = patterns('mysite.news.views',
    (r'^$', 'front_page'),
    (r'^archive/(\d{4})/$', 'year_archive'),
    (r'^archive/(\d{4})/(\d{2})/$', 'month_archive'),
    (r'^album/(\d+)/$', 'album_detail'),
)
urlpatterns += patterns('',
    (r'^comments/', include('django.contrib.comments.urls')),
    (r'^admin/', include('mysite.admin_site.urls')),
)

URLs are configured for the entire project. This file maps URLs to view functions.

Regular expressions are used to match against the requested URL.

I am not a big fan of regular expressions because they are often used in places that they really shouldn't be, but URL mapping is a very good use for them.

The regular expressions are all compiled in advance so they are very fast, even if you have many patterns.

View functions are defined for each URL pattern. groups in the regular expressions are passed to the view functions as parameters. If you like you can name groups in the regular expressions to pass parameters by name to the view functions.

applications can define their own URL handling and you may delegate parts of your URL space with the include function. Here comments and admin will pull in URLs from those applications.

You can specify extra parameters to functions here if you want to reuse the same view function in different ways.


urls.py Reversed

from django.core.urlresolvers import reverse

# mysite.music.views
#  .front_page() → '/'
#  .year_archive(year) → '/archive/%04d/'
#  .month_archive(year, month) → '/archive/%04d/%02d/'
#  .album(id) → '/album/%d/'

#
#  django.contrib.comments.views... → '/comments/'... 
#  mysite.admin_site.views... → '/admin/'...

Django automatically does reverse mappings from view functions and parameters to absolute URLs. This way you are not tied to a single URL scheme, and you don't have to repeat your particular URL mappings all through your code.

Applications and templates can query this reverse mapping any time they they need to create a link or redirect.

If there are multiple URLs that map to the same view function you can name each URL in the configuration and use the URL name instead of the view function name to get the preferred reverse mapping.


View Code

from django.shortcuts import get_object_or_404, render_to_response
from models import Album

def album_detail(request, album_id):
    a = get_object_or_404(Album, id=int(album_id))
    return render_to_response('/music/album_detail.html', {'album': a})

Those URLs map to view functions responsible for gathering data and sending responses to the user.

Django views are passed the request object and any parameters that were grouped in the project url configuration. URL escaping is automatically handled so you get the actual values passed without any extra effort. Though they will always be strings, even if your regular expressions has only digit values.

Views need to either return an HTTP response object or raise a 404 error.

This function uses a shortcut method to respond with a 404 if this object doesn't exist. It uses another shortcut to pass a model to a template to be rendered, then return the result.

The shortcuts are part of Django's pragmatic design. They are convenience functions that span the otherwise separated Model, View and Templates layers, where it makes sense to do so.


Form Code

from django.forms import ModelForm
from models import Album

class AlbumForm(ModelForm):
    class Meta:
        model = Album

Any web developer will tell you that form handling is very important in web applications, and Django does most of the heavy lifting for you.

This is a form based on the existing Album model. This form will take fields based on the ones that are defined in the model.

Form fields are similar to model fields, but instead of knowing how to map Python data to SQL columns, they map Python data to HTML strings and input fields.

Django forms don't need to be tied to models this way, instead you can specify a list of fields just like in our model, here we are saved from repeating ourself in this common case.

Forms can be unbound, like when you create a blank form, or bound to data passed in. Bound forms keep a copy of both the original text data and cleaned data that has been turned into Python objects like numbers, Unicode strings, boolean values and dates.


Form Validation

Form fields, like EmailField and DecimalField have validation built-in and know how to clean their input data.

If you need extra restrictions it is easy to add your own validation to any of the existing Field types. You can also make one field depend on another to be valid, such as when having a user enter their password twice.


Form Widgets

Form fields display simple HTML input widgets by default, but you can create any interface you like.

The admin app uses some slick HTML and JavaScript to display calendars for their date time fields and vertical and horizontal interactive multiple select fields.

This has also been used to create image upload fields with thumbnail previews.

GeoDjango uses this to display interactive JavaScript maps that can be used to draw paths and regions, instead of keying in lists of coordinates.

Because Forms are not tied to models, the data entered in one form field could be stored in many fields or across many models.


View Code with Forms

from forms import AlbumForm

def create_album(request):
    if request.method == 'POST':
        form = AlbumForm(request.POST)
        if form.is_valid():
            # Success!  Create our new Album
            album = form.save()
            redirect_url = reverse(album_detail, args=[album.id])
            return HttpResponseRedirect(redirect_url)
    else:
        form = AlbumForm()
    return render_to_response('/music/create_album.html', {'form': form})

How do we use Forms?

View code is responsible for handling forms. Here is an example of what a create album view might look like.

First the user will arrive at the create album page by following a link resulting in a GET request. So this view creates a blank, or unbound, album form and passes it to a template to be rendered.

Notice we don't need to know what fields are involved, what types of widgets they need or what the defaults are. All of that is handled by our form

Our template can choose to let the form display itself as a table or set of paragraphs. No repeating field details necessary there, unless you want to choose exactly how to position each field on the page.


Correcting Form Errors

The user fills out all the values, and maybe forgets to enter some values or mistypes something and clicks submit.

Django has parsed the values sent in the post request and stored them in request.POST. We pass all the values from the request to our form. Here we only have one form but if we had more than one form on the same page we could specify a prefix to distinguish them.

The form collects the values, mapping each to the correct field and finds a few problems. So it now has the values they entered, and some helpful error messages.

The form is not valid so we render our page again, this time with the values they entered and the errors on each field displayed.


Processing Form Data

The user corrects their errors and submits again. This time our form data is valid.

We used a Form created from a model, so Django gives us a save method we can use to create a new object from data in the form.

Next we use the handy URL reversing feature to find the full URL to our album detail view for this new album object.

Finally, like any good web developer we send an HTTP redirect after handling this POST request. This way the user won't get an unpleasant “do you want to resubmit your data?” pop-up if they click their BACK button.

That is the general format of view code form handling, and as you can see Django gives you the common functionality users expect with very little code.

This example is completely safe from SQL and JavaScript injections and has helpful error messages when invalid values are entered.


Formsets

Formsets are new in Django 1.1, they will display multiple forms as part of a single larger form, like the admin interface can with inline models.

Think of them like a data grid, with many rows being edited at the same time. This is great for data entry or making changes across many objects.

Formsets can present an interface to let you reorder the items in the list as well as deleting items in the list.

They know which forms have been edited and do not return blank or unchanged forms.

Like regular forms, you can have multiple formsets on a single page by specifying a different prefix for each one.


File Uploads

Django is very good with file uploads. By file uploads I mean uploading large files, because small files aren't interesting.

The default is to store files on the filesystem, if they're larger than a couple megabytes they are streamed to a temporary file before being saved.

But, If you love having everything in your database, you can set up your file storage to put uploaded files there. If you need to store files over the network, you can do that too.

For handling large files you can insert an upload handler in the chain of handlers that updates a JavaScript progress bar in the client's web browser, kind of like Django middleware for file uploads.

If you wanted to use Django to create the next Youtube you could use a custom handler that transcodes video as it is being sent, before it gets saved on the server, possibly with frame grabs appearing in the client's browser as it uploads.

Upload handlers are project wide, but they can also be changed on a per-view basis.


return to Contents

Template System

The last major part of Django is its Template system. Other MVC frameworks would call this part the view, because the templates are responsible for determining how the data is presented to the user.

Sometimes even the best programmers aren't very good at creating attractive, usable web interfaces. For that reason Django's Template system is targeted towards web designers more than programmers.

This is a fundamental difference between Django and languages like PHP, where all the powerful programming tools are mixed right in with the HTML page layout.

For example, In templates web designers don't need to worry about whether they are accessing a dictionary item, doing an attribute lookup, making a method call or getting a list item. All of them look the same and depend on the data that they are given.


Template Language

Django templates escape all text displayed by default. This is the right design, it means you have to go actually out of your way to create an unsafe interface.

Templates give you a simple language with conditional statements, looping but no general purpose code execution. Templates can extend common templates and include the complete contents of one template in another.

The template language is not Python mixed in with HTML, even though it looks a little Python-like. For the times when you need to write code you can export your code as custom tags and filters for the template authors to use.


Template Tags and Filters

These are the tags and filters available to template authors. There are tags for looping and condition testing. There are filters for formatting dates, numbers and lists, and for all sorts of other common tasks.


Template “base.html”

<html>
<head>
    <link rel="stylesheet" href="style.css" />
    <title>{% block title %}My music site{% endblock %}</title>
</head>
<body>
    <div id="sidebar">
        {% block sidebar %}<a href="/">Home</a></li>{% endblock %}
    </div><div id="content">
        {% block content %}{% endblock %}
    </div>
</body>
</html>

This is what a simple base template might look like.

We define a number of blocks like title, sidebar and contents here. These blocks may have some default content. Templates that extend this can replace the contents of the blocks.

This example will have only one level of nesting, but you can go as deep as you need to to reuse parts of your layout.


Template “musician.html”

{% extends "base.html" %}
{% load album_extras %}

{% block title %}Musician: {{ musician.name | upper }}{% endblock %}

{% block content %}
    <p>Instrument: {{ musician.instrument }}</p>
    <p>Bio: {{ musician.bio | truncatewords: 500 | linebreaks }}</p>
    {% for album in musician.albums %}
        <p>{{ album.name }} ({{ album.release_date | date: "Y" }})
           {{ album.num_stars | drawstars }}</p>
    {% endfor %}
{% endblock %}

This template extends the last one.

We put the musician's name in the title, all in uppercase.

In the content block we then display information about the musician. The bio might be long, so we truncate it at 500 words and convert the newlines into HTML linebreaks. Notice how we can chain our filters by putting one after the next.

We then loop through the musician's albums, showing each album's name, the year it was released and the rating as a number of stars.

Django has no built-in filter called drawstars, this is something we could write ourself with a little bit of Python code, and we loaded that filter at the top from our album extras module.


return to Contents

Resources

Django

Python

Myself

Here are some places you can go for help with Django. The documentation on the main site is expansive and thorough. The Django Project site also has links to the community mailing lists and IRC channels for getting help.

There is a community-edited Django book that covers each topic with a different angle than the main docs. The documentation on the main site is organized more as a reference, while the book takes you through everything.

Everything you need to know about Python is available from the Python web site.

There are also hundreds of people like myself available to help you with Django.

Tags: Django Ottawa Software Python FOSSLC