An Introduction to Django - Part 2


Nathan Osman's Gravatar

Nathan Osman
published June 24, 2013, 11:56 a.m.


In my last article, I went over the process of getting Django set up and outlined a sample application that would allow users to post short status updates and view updates from others. The article briefly mentioned views but I ran out of space to actually describe them. Thus, we will begin this article by creating some views.

But First...

Before we actually create some views, we can make things easier for ourselves by inserting some "dummy" data into the database. Although the admin interface would make that easy, executing some raw SQL would make that even easier. So open up a terminal and launch the MySQL prompt:

mysql -u trackme -p

You will be prompted for the trackme password (if you can't remember it, check trackme/settings.py). Now run the following SQL commands:

USE trackme;
INSERT INTO auth_user (id, username) VALUES
    (100, "Aaron"),
    (101, "Bob"),
    (102, "Chris");
INSERT INTO updates_update (user_id, body, creation_date) VALUES
    (100, "Went to the beach today. Lots of fun.", "2013-05-08 20:47:12"),
    (101, "Just heard that Aaron went to the beach. Makes me want to go.", "2013-05-08 21:12:09"),
    (102, "Bought a new TV. Motion is much more fluid now.", "2013-05-09 12:56:18"),
    (101, "Heard that Chris bought a new TV. Makes me want to buy one.", "2013-05-09 13:19:24");

Now that you've seen how to use SQL to insert data into the database, please immediately forget that this is possible. This is something you should not be doing under normal circumstances as it can lead to all sorts of problems. Instead, use the admin interface.

Creating Views

If you recall from my first article, I described an individual view as the "middle man" that retrieves data from the model(s) and passes it along to the templates. Since we haven't discussed templates yet, we're going to begin by creating a view that directly returns HTML.

URLs

But before we even do that, we need to tell Django which URLs map to which views. We do this by editing trackme/urls.py. Remember that in the last article, we added a single entry for the admin interface. Now we will add an entry for the home page and the URLs in the updates app. You can do that by replacing the file with the following contents:

from django.conf.urls import patterns, include, url
from django.contrib import admin

admin.autodiscover()

urlpatterns = patterns('',
    url(r'^$',        'updates.views.index'),
    url(r'^updates/', include('updates.urls')),
    url(r'^admin/',   include(admin.site.urls)),
)

The second entry in urlpatterns references updates.urls, which does not exist. So, create the file updates/urls.py and insert the following contents:

from django.conf.urls import patterns, url

urlpatterns = patterns('updates.views',
    url(r'^new/$', 'new'),
)

In summary, we have added two URLs to the project:

  • / - the home page which displays a list of updates
  • /updates/new/ - a page that enables users to post a new update

These URLs are mapped to views named index and new respectively.

Creating Views (For Real)

In case you haven't guessed yet, the views should be created in updates/views.py. Each view is represented as a function that is provided with an HttpRequest object and is expected to return an HttpResponse object. So, open the file and insert the following contents:

from django.http import HttpResponse

def index(request):
    return HttpResponse('Index page.');

def new(request):
    return HttpResponse('New update page.');

Now execute the runserver command like we did in the first article and view the / and /updates/new/ URLs in your browser. If all went well, you should see the appropriate status message displayed.

This is great - but what about displaying data from the model? In order to do that, we simply import the Update model into the view:

from models import Update

Now we can retrieve all of the updates in the index function (sorted newest to oldest) with the following line:

updates = Update.objects.all().order_by('-creation_date')

We can print them as an HTML list by iterating over the updates:

updates_html = '<ul>%s</ul>' % ''.join(['<li>%s</li>' % u.body for u in updates])

(See - isn't Python beautiful?) Putting it all together, the index function now looks like this:

def index(request):
    updates = Update.objects.all().order_by('-creation_date')
    updates_html = '<ul>%s</ul>' % ''.join(['<li>%s</li>' % u.body for u in updates])
    return HttpResponse(updates_html)

Templates

What we've done so far involved hard-coding HTML in the view functions. This is bad. First of all, this leads to a lot of code duplication. Secondly, it's good practice to keep the presentation of data separate from the view that retrieves the data.

Thankfully Django solves both of these problems with templates. Django is compatible with a number of templating languages, but also ships with its own built-in templating language. We will use Django's template system since it is very easy to use and well integrated with the rest of the framework.

Templates support a form of inheritance, meaning that we can group common HTML in a single file and reference it from other templates. We will begin by creating a template that contains HTML common to all pages.

Create a directory named templates in updates and create a file named base.html in that directory. Insert the following contents:

<!DOCTYPE html>
<html>
<head>
  <title>{% block title %}Track Me{% endblock %}</title>
  <style>
    body {
      font-family: Arial, Helvetica, sans-serif;
      margin: 0;
    }
    header h2 {
      background-color: #eee;
      border-bottom: 2px solid #777;
      color: #bbb;
      margin: 0;
      padding: 8px;
    }
    #contents {
      padding: 8px;
    }
  </style>
</head>
<body>
  <header>
    <h2>Track Me</h2>
  </header>
  <div id="contents">
    {% block contents %}{% endblock %}
  </div>
</body>
</html>

This looks like normal HTML with the exception of the {% block xxx %}{% endblock %} template tags. These signal to Django that other templates can insert their own content in those places, which we will do shortly. The title block already has some content - this will be used in the event that the other template does not override the block.

Now create a directory in templates named updates. Inside that directory, create a file named index.html and insert the following contents:

{% extends "base.html" %}

{% block title %}{{ block.super }} - Updates{% endblock %}
{% block contents %}
  <p>Here are recent updates:</p>
  <strong>not implemented</strong>
{% endblock %}

This template is quite a bit different! The first line indicates that this template extends the base template we created earlier. Therefore, all we need to do in the rest of the template is decide which blocks we want to override from the base template. When overriding the title, we reference the default text that already exists (which is "Track Me") and stick " - Updates" on to the end. The item surrounded by {{ }} is known as a template variable.

The next step is to instruct the view to render the template. Return to updates/views.py and add the following import:

from django.shortcuts import render

Now replace the index view with the following code:

def index(request):
    return render(request, 'updates/index.html')

Take a look at the home page in your browser - doesn't that look nice? We are making use of Django's render function which expects the HttpRequest that our view function receives and the name of a template to render. Django searches for this template within each app's templates/ directory.

Template Variables

We still need to display the status updates on the home page, so we need to pass the list of updates from the model to the template. We do this with a third parameter to render: a dictionary mapping variable names to values:

def index(request):
    return render(request, 'updates/index.html', {
        'updates': Update.objects.all().order_by('-creation_date'),
    })

Here we pass a single variable named updates to the template that contains the status updates to be displayed. Edit the contents block in the template:

{% block contents %}
  <p>Here are recent updates:</p>
  <ul>
    {% for update in updates %}
      <li>{{ update.body }} - <strong>{{ update.user }}</strong></li>
    {% endfor %}
  </ul>
{% endblock %}

In this template block, we use the for template tag to enumerate over each update. Everything in between the {% for %} and {% endfor %} is rendered once for each item in updates. Inside the loop, the variable update represents an instance of the Update model. We can access the fields in the model using a . followed by the field name.

Wrapping Up

Well, once again we have come to the end of today's article. We were able to cover views and Django's template language. Hopefully you are starting to see just how flexible Django can be.

We'll probably take a look at forms in the next article.


NextAn Introduction to Django - Part 3