Main Content

Django - separating the HTML from the view / model

Archive - Originally posted on "The Horse's Mouth" - 2011-01-20 13:30:16 - Graham Ellis

This is the third section on Django - the Python based web framework. See first Step 1 and Step 2.

Let's add another view of our data to our Django app - using DRY techniques, this will be a short piece of coding but I'll take the opportunity to add in and explain a few more features that you'll probably find yourself using:

Model

No change - we're working with the same data structure!

URL Mapping

The last line in url.py has been added:

  (r'^hosts/$', 'nets.views.index'),
  (r'^hosts/index\.htm', 'nets.views.index'),
  (r'^hosts/(?P\w+)\.htm','nets.views.single'),
  (r'^hosts/(?P\w+)','nets.views.integral')


That's a new view, with an added URL which does not include ".htm" or ".html" on the end. Opinions vary as to whether you should (or should not) add .html ... and I've chosen for this example to use the variant we hadn't previously used to provide an extra URL. We've added a new view ...

[complete source - urls.py]

The view

  def integral(request,system_id):
   
    syl = System.objects.all()
    if (system_id == 'index'):
      t = [('home','http://www.wellho.net'),('hostindex','index')]
    else:
      t = [('home','http://www.wellho.net'),('hostindex','index'),(system_id,system_id)]
   
    fill = Context({'sylist':syl, 'year' : year,
      'expand' : system_id, 'trail': t,
      'entitled':'Integrated page using template code'})
    return render_to_response('nets/integrate.html',fill)


The really important thing here is that we have removed ALL of the html from this code, and in now purely translates the information held in the model into values that we're going to make use of in our template to view the the data - that's where the HTML should rightfully be!

In order to remove the HTML, we have structured the data that we need to pass to the template for formatting into lists and tuples in the context. And it's such a frequent requirement to read, complete and return a template that we've used a shortcut called render_to_response to do the work in a single step. For that to work, we need to pull in a new include file:
  from django.shortcuts import render_to_response.

[complete source - view.py]

The Template

We've retained elements such as
  <a href=http://www.wellho.net/course/python.html>see here</a><br />
    Copyright &copy; {{year}}, Well House Consultants

which are just dropping values into the page, but we have taken away all elements of the form
  <div class=trail>{{trail|safe}}</div>
exactly because they were not safe (or at least they weren't wise) - these are the elements which include various bits of HTML which we generated within out Python, rather than keeping them purely at the template level.

Let's see how we handled that trail. Previously we passed in some HTML like:
  'home -> <a href=index.html>hostindex</a> -> '+system_id,
but now we're going to pass in a Python structure:
  [('home','http://www.wellho.net'),('hostindex','index'),(system_id,system_id)]

And in the template, we'll replace our simple variable place holder with the following code:
  {% for element in trail %}
    &nbsp;<a href={{element.1}}>{{element.0}}</a>
  {% endfor %}

Which places the HTML firmly (and only) in the template.

We'll do something similar with the list of systems, and add into there the ability to highlight an item so that we can use a single template for the listing, and also for individual pages - and that will bring in progamming-style tags like if and while.

  {% for h in sylist %}
  {% if h.name == expand %}
    <br /><center><table border=3 cellpadding=10 width=80% bgcolor=#FFCCAA><tr><td>
    <li>{{h.name}} at {{h.ip}}</li>
    Status: {{h.about}}
    </td></tr></table></center><br />
  {% else %}
    <li><a href={{h.name}}>{{h.name}}</a> at {{h.ip}}</li>
  {% endif %}
  {% endfor %}


It's "Dry" - it's non-repeating. The changes are distributed over the three files - the template, the view.py and the urls.py (and they could bring changes to the model too), but each change is made only once. And we've now got excellent separation from the look and feel, represented by the template, and the model, with the view in between providing the manipulation code for the data.

[complete source - template.html]

Let's see some sample output: