At the Forge - Django Views and Templates

 in
Getting started with Django views and templates, with an eye to the Django way of working.
More Complex URLs

Things are not that interesting if all we have is a single method and if it always does the same thing. For a blog application, we'll presumably want to be able to read a particular posting, or postings, on a particular day. And although we'll get into the model for our blog application next month, it stands to reason that this means we'll need to be able to request blog postings by an individual ID or by date.

Our current URLConf doesn't handle such situations. Indeed, Django requires that we explicitly indicate each possible URL that a user might request and how that URL should be handled. So although we have taken care of blog/, we need to handle such URLs as:

^blog/ID

Luckily, it should be pretty straightforward for us to set up a regular expression that captures such functionality. If we open urls.py once again, we can add another statement:


(r'^blog/(?P<post_id>\d+)/$', 'mysite.blog.views.view_one_posting')

Here, we see something a bit strange and different, namely the use of capturing parentheses, along with ?P and names inside angle brackets. We tell URLConf that whenever we receive a URL that looks like /blog/NUMBER, with one or more digits, we should invoke view_one_posting. Moreover, because we have captured the digits (\d+, in regular expressions) with the name post_id, view_one_posting will be invoked with an additional parameter of post_id.

In other words, once we've modified urls.py to include the above mapping, we now can go back to views.py and say the following:

def view_one_posting(request, post_id):
return HttpResponse("You asked for post '%s'" % post_id)

Then, we can go to http://69.55.232.87:8000/blog/5, and in our browser, we get the following response:

You asked for post '5'

Notice that in the view_one_posting method, we used %s (for strings) to render post_id, rather than %d (for integers). This is because parameters and URLs are passed as strings, rather than integers or other data types. We could, of course, get around this problem by converting the string to an integer, but for our purposes right now, this is enough.

Perhaps this goes without saying, but each method in our Django application is a Python method whose output just happens to be sent to the user's Web browser. You can use any number of Python libraries that you want, and even access databases, filesystems and remote computers. So long as the output is returned as a legitimate HTTP response, your method will work. The application we are building in these examples represents the tip of the iceberg, as far as implementation complexity is concerned.

Templates

If we wanted to, we could create a whole Web site using nothing more than the tools we've already seen. However, it quickly would become difficult, and even tedious, to do so, putting all of our output strings as parameters to HttpResponse. The real solution is to put the HTML in external files, interpolating variable values as necessary.

There are a lot of different templating solutions out there, and each has its advantages and disadvantages. The most common type is that used by ASP, PHP, JSP and ERb—the last of which is part of Ruby on Rails—in which the code is interspersed with the HTML. This type of template can be extremely easy for programmers to work with, but it can cause trouble when nonprogramming designers are involved, or if you just want to have a complete separation between code and design.

Such templates can be used by Django, but they are not the default. Rather, Django uses templates that are similar in many ways to the Smarty system for PHP, which avoids the use of actual code in the template by introducing its own, deliberately limited language. So long as a view can pass values to a template, there's no real need for a full-blown programming language inside of a template. It's probably enough to have if/then statements and some basic loops.

This is what Django's default template system provides. In order to use templates, we first modify the settings.py file that sits in the project's main directory. We want to set the TEMPLATE_DIRS variable, giving it a list of one or more directories in which our templates might be found. For example, we could set it to:

TEMPLATE_DIRS = (
"/opt/atf/mysite/templates"
)

With this in place, now we create the appropriate directories:

$ mkdir -p /opt/atf/mysite/templates/blog

And, then we create, in that blog subdirectory, a new HTML file, which we call view_one_posting.html:


<html>
<head>
    <title>One post</title>
</head>

<body>
    <p>This is the "view_one_posting" template.</p>
</body>
</html>

Now, we modify views.py, such that our view_one_posting method can invoke this template. We add the following import statement at the top of the file:

from django.template import Context, loader

Then, we modify view_one_posting to be:

def view_one_posting(request, post_id):
t = loader.get_template('blog/view_one_posting.html')
c = Context({})
return HttpResponse(t.render(c))

Our template is loaded into the variable t, and the context—that is, the variable values we want to pass to our template—is bound to the variable c. We then tell the template to render itself within the context c. In this particular example, we aren't passing any variables in the context, so it is represented by an empty dictionary.

And, if we reload the URL /blog/5 on our site, we should see the following in the browser window:

This is the "view_one_posting" template.

That's certainly better than what we had before, in that the contents of the template are easier to handle (by programmers and designers alike) than a string inside of a view method. But, how do we pass variables to be interpolated?

The answer is quite simple. In the view method, we can pass any variable we like to the template using the context dictionary, in which the keys are the passed variable names, and the values are the passed variable values. So, we can say:

def view_one_posting(request, post_id):
t = loader.get_template('blog/view_one_posting.html')
c = Context({'post_id': post_id})
return HttpResponse(t.render(c))

If we want to see this value in our template, we can view it in double curly braces:


<html>
<head>
    <title>One post</title>
</head>

<body>
    <p>This is the "view_one_posting" template.</p>
    <p>The post_id is {{post_id}}.</p>
</body>
</html>

And, sure enough, when we render this template, we get:

This is the "view_one_posting" template.

The post_id is 5.

______________________

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Thanks!

DessertLover's picture

Good article. I am newbie to framework web development. I found django to be very picky. And this article helped me get started.
Suggestion for other newbies: Follow the article strictly for the first time. After you are successful in the first pass, make changes to suit your own needs.

Minor Syntax Point

Milan Andric's picture

Great article, I like your approach, I'm a fan of your column and LJ in general.

One thing that you might want to fix is the function definitions for the views. The Python code should be indented properly otherwise it won't run. Maybe my browser or the rendering was off, but you might want to check it, or just mention it.

Thanks for taking a tour of Django and writing about it. I've been using Django for a little while now and enjoy working with it very much.

Webinar
One Click, Universal Protection: Implementing Centralized Security Policies on Linux Systems

As Linux continues to play an ever increasing role in corporate data centers and institutions, ensuring the integrity and protection of these systems must be a priority. With 60% of the world's websites and an increasing share of organization's mission-critical workloads running on Linux, failing to stop malware and other advanced threats on Linux can increasingly impact an organization's reputation and bottom line.

Learn More

Sponsored by Bit9

Webinar
Linux Backup and Recovery Webinar

Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.

In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.

Learn More

Sponsored by Storix