Monday 1 June 2009

Middleware: Like An Onion But With Less Crying

This week will be the first part of a look into the middleware system that Django provides. This is an extremely powerful system that allows you to create pieces of code to run on each and every request that comes in.

If you've done even a small project in Django you'll no doubt have come across middleware, possibly without even realising it. The main starting point is located in the settings.py for a project. If you inspect this file you should find an entry similar to:

#...
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
)
#...

This tuple of classes is what Django will refer to when it loads up its middleware and not only defines the items that will be loaded but also the order in which they will be executed. The items in the example given here represent a very simple set and are all that is needed for a very basic site. CommonMiddleware provides some core properties that are used by the other classes, SessionMiddleware is responsible for attaching the session object to a request (allowing you to store information from one request to another) and also for ensuring that the session is saved back to the database once the request has completed being processed. Finally AuthenticationMiddleware is responsible for adding things like the user object to the request - not a necessity as such but still extremely handy none the less.

There are four separate phases that middleware can act on. Any individual item of middleware can act on any number of these phases and executes each middleware class in a specific order which are:


  1. The process_request phase - middleware is executed in the order specified;

  2. The process_view phase - middleware is executed in the order specified;

  3. The process_response phase - middleware is executed in the reverse of the order specified;

  4. The process_exception phase - middleware is executed in the reverse of the order specified;

The process_request phase is the earliest available point that middleware can be called. After Django receives a request and generates a request object then it calls all the middleware classes that implement the process_request function passing them the newly created request object. This phase is generally the best place to run any essential checks or add things to the session that would cause the system to break if they were missing.

The process_view phase is the second phase to run. It occurs after the request has been processed and the view that will be run has been determined. At this point all the middleware classes that implement the process_response function are called and given access not only to the request object but also to the view object that is about to be executed along with any arguments that are going to be passed to the view object. This phase is a good place to put checks where you may want to redirect the user to a page asking them to log in, redirecting the user to somewhere requesting them to complete filling in profile details or initialising a log entry (as you know at this point more information about what the user is doing).

The process_response phase runs at the point after a view has been executed and Django has been given a response object to display back to the user. All classes that implement the process_response function will be provided with the original request object as well as the response object that was generated. This phase is good for things like tidying up anything that should be stored between requests or completing and writing out log entries.

The process_exception phase is a slightly special case that runs whenever an exception cuts in and interrupts the execution of a request. All classes that implement the process_exception function will be provided with the original request and the exception that has been thrown. This phase is good for logging any problems that have occurred and for trying to either send the user to a friendly error page or to try and process something that wont cause an error instead.

Any of the functions that are implemented in the class have two choices of what to return. If they return None then that processing will continue with the next item of middleware in order otherwise they can return a response object which will then be used straight away, if the phase returning the new response is the request, view or exception phase then things will jump straight to the response phase and only middleware in that phase will be run subsequently. If the phase returning the new response is the response phase then no more middleware will be run.

A quick note on the ordering - be aware that during the response and exception phase middleware is run in a reverse order. This means that for things like the session middleware. Anything listed after it in the settings.py file can add things to the session at any point and it will still be saved since the session will be set up early on (as the request and view phase run in the normal order) and it wont be saved until the latest possible moment.

That concludes this weeks look at what goes into the middleware. Next week I will be providing some examples of some interesting things that you can do with middleware.

Monday 25 May 2009

Pagination: Splitting Your Data Into Bite-Size Chunks For Easier Digestion

This week it's time for something nice and simple, Django has some very useful tools for handling pagination and that is what will be covered.

If you have a site that is constantly having to return long lists of results/data, then sooner or later you are probably going to want to look for a way to easily split that data over several pages so that the user can view it in easier to read chunks. Django's pagination framework is one quick and easy answer to such a problem. Take the example given below:

#File: views.py
from django.shortcuts import render_to_response
from django.template import RequestContext

def pagination_test(request, word_string):
word_chars = list(word_string)
anagrams = []
for anagram in _generate_anagrams(word_chars):
anag = "".join(anagram)
if anag not in anagrams:
anagrams.append(anag)
response_dict = {'anagrams':anagrams,
'original':word_string,
}
context_instance = RequestContext(request, response_dict)
return render_to_response('pagination_test.html',
context_instance=context_instance)

def _generate_anagrams(word_chars):
# Warning - This function is horrendously inefficient and should
# not really be used for words longer than 6 characters.
char_count = len(word_chars)
if char_count == 0:
yield []
elif char_count == 1:
yield [word_chars[0]]
else:
for char_index in range(len(word_chars)):
lone_char = [word_chars[char_index]]
other_chars=word_chars[:char_index]+word_chars[char_index+1:]
for others in _generate_anagrams(other_chars):
yield lone_char + others
{# File: pagination_test.html #}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Anagrams of {{ original }}</title>
</head>
<body>
<h1>Anagrams of {{ original }}</h1>
<ul>
{% for anagram in anagrams %}
<li>{{ anagram }}</li>
{% endfor %}
</ul>
</body>
</html>
#File: urls.py
from django.conf.urls.defaults import *

urlpatterns = patterns('views',
# Other urls ...
url(r'^test/pagination/(?P<word_string>.+)/$,
'pagination_test', name='pagination_test')
)

Here we take a string as an input from the user and return all the possible unique combinations that can be achieved by rearranging its characters. For short strings this produces manageable numbers of results but even getting up to five character strings the list starts to increase in size dramatically (something like order n^2). What we can do here though is put it through Django's pagination framework like so:

#File: views.py
def pagination_test(request, word_string, page_index=1):
word_chars = list(word_string)
anagrams = []
for anagram in _generate_anagrams(word_chars):
anag = "".join(anagram)
if anag not in anagrams:
anagrams.append(anag)
paginator = Paginator(anagrams, 10, 5)
try:
page = paginator.page(page_index)
except (EmptyPage, InvalidPage), e:
page = paginator.page(paginator.num_pages)

response_dict = {'anagrams':page,
'original':word_string,
}
context_instance = RequestContext(request, response_dict)
return render_to_response('pagination_test.html',
context_instance=context_instance)

def _generate_anagrams(word_chars):
# Same function as before

In the code above, paginator = Paginator(anagrams, 10, 5) sets up a paginator on our results list. The first argument to Paginator says that we want the items in the anagrams list to be split into pages. The second argument says that each page should contain ten results, whilst the third argument says that if there would not be at least five results on the final page they should be added to the one before (meaning the last page will contain anything from five to fourteen results). The other addition is responsible for saying which page of results should be displayed, first of all trying the value passed in and if it's a non-existent page then using the last available page.

{# File: pagination_test.html #}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Anagrams of {{ original }}</title>
</head>
<body>
<h1>Anagrams of {{ original }}</h1>
<div>
Results {{ anagrams.start_index }}
to {{ anagrams.end_index }}
of {{ anagrams.paginator.count }}
</div>

<ul>
{% for anagram in anagrams.object_list %}
<li>{{ anagram }}</li>
{% endfor %}
</ul>
<div>
{% if anagrams.has_previous %}
<a href="{% url paged_pagination_test original anagrams.previous_page_number %}">Previous</a>
{% endif %}
{% if anagrams.has_next %}
<a href="{% url paged_pagination_test original anagrams.next_page_number %}">Next</a>
{% endif %}
</div>

</body>
</html>

In the template the main additions are for saying which results we're viewing through the use of start_index and end_index which are functions that belong to pagination page objects representing the first and last indices of the results on display and count which is a function on the paginator itself representing the total number of results.

In the loop that iterates over the results, it needs to be told to iterate over the object_list attribute of the pagination page as this represents the actual results to be shown on this page.

Finally the section at the bottom of the page is responsible for adding Next and Prev links. has_previous and has_next are pagination page functions that do exactly what you would expect and tell you if there is a page of results available either before or after the current one. Meanwhile previous_page_number and next_page_number return the indices of the page before and page after.

#File: urls.py
from django.conf.urls.defaults import *

urlpatterns = patterns('views',
# Other urls ...
url(r'^test/pagination/(?P<word_string>[^/]+)/$',
'pagination_test', name='pagination_test'),
url(r'^test/pagination/(?P<word_string>.+)/(?P<page_index>\d+)/$',
'pagination_test', name='paged_pagination_test'),

)

An extra line in the urls file picks up any visits to our test url that has a page index included on the end.

If you now try visiting this with a shortish word like "hop" you should be presented with a single page containing all six possible anagrams. Try again with "shop" and you should get two pages worth of results with ten on the first page and fourteen on the second. Finally try with "shops" and you should get six pages of ten results.

That's it for this weeks tutorial, tune in again next week where I will be starting to look at Django's middleware system.

Sunday 17 May 2009

Signals: Letting People Know When You've Got Something To Say

This week marks a break away from the topic of templates and instead will be focussing on signals.


Signals are Django's way of allowing you to listen out for certain events that either occur by default in the Django code or that have been specified by an application developer. The signals provided by default are explained in a fair amount of detail in the Django Built-in Signal Reference (djangoproject.com) so I won't go into too much depth on what they do, but they are as follows (skip to the first example):



pre_init
Occurs at the start of the process of initialising a model;

post_init
Occurs at the end of the process of initialising a model;

pre_save
Occurs at the start of the process of saving a model;

post_save
Occurs at the end of the process of saving a model;

pre_delete
Occurs at the start of the process of deleting a model;

post_delete
Occurs at the end of the process of deleting a model;

class_prepared
Internally used, occurring when a model class has been registered with Django;

post_syncdb
Occurs after syncdb installs an application;

request_started
Occurs just before Django starts to process a request;

request_finished
Occurs just after Django processes a request;

got_request_exception
Occurs when Django encounters an exception whilst processing a request;

template_rendered
Occurs only whilst running tests when Django renders a template;

comment_will_be_posted
Occurs if you have Django's comment app installed and a comment is just about to be saved;

comment_was_posted
Occurs if you have Django's comment app installed and a comment has just been saved;

comment_was_flagged
Occurs if you have Django's comment app installed and a comment has had a flag set (i.e. a moderator just approved it).


For my first example this week I'm going to be looking at the post_save signal and how it can be used to ensure that a User object always has a profile instance associated with it.


# File: test_signal/models.py
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save

class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
allow_our_emails = models.BooleanField(default=False)
allow_other_emails = models.BooleanField(default=False)

def ensure_profile_exists(sender, **kwargs):
if kwargs.get('created', False):
UserProfile.objects.create(user=kwargs.get('instance'))

post_save.connect(ensure_profile_exists, sender=User)

# File: (somewhere in) settings.py
AUTH_PROFILE_MODULE = "test_signal"

As you can see this defines a fairly simple profile model (supposedly for storing a user's e-mail preferences), the key part of this comes from the function ensure_profile_exists. With all signals it can only be guaranteed that the sender keyword argument will be present therefore we also need to catch everything else using **kwargs. Inside the function we use the fact that post_save is supposed to provide the arguments created and instance to check if the save was for a new object and if so to assign the created instance to the user property of a freshly created UserProfile object.


Finally we connect our function to the post_save signal with instructions to only receive signals when the sending class is User


If the test_signal app is now added to the list of installed apps in settings.py then all new users created will now have an associated profile created also.


The second example for this week demonstrates how you can create your very own signals for code to listen to.


# File: test_signal/signals.py
from django.dispatch import Signal

form_post_received = Signal(providing_args=["path","arguments",]

Here we tell Django that we want form_post_received to be a signal and that it should provide the arguments path and arguments. Now if we add the following:


# File: test_signal/views.py
from signals import form_post_received
from django.conf import settings

def view1(request):
if request.method == "POST":
form_post_received.send(sender=request, path=request.path, arguments=request.POST)
# Continue to do stuff with the form.

def view2(request):
if request.method == "POST":
form_post_received.send(sender=request, path=request.path, arguments=request.POST)
# Continue to do stuff with the form.

def log_form_received(sender, **kwargs):
print "Form Received at %(path)s with arguments %(arguments)s" % kwargs

if settings.DEBUG:
form_post_received.connect(log_form_received)

When pointing views at view1 and view2 whenever the server is in debug mode extra information should be output to the terminal informing us of whenever a form has been posted to either view.


This is done by calling send on our signal along with an argument for sender, in this case we use the request that triggered the view, along with values for each of the other arguments we specified. Django then passes them on to each function that has connected to our signal, which in this case is our logging function (but only when the server is in debug mode, otherwise connect will never be called).


That concludes this weeks tutorial on signals. Next week we shall be looking at the useful functions Django has built in for performing pagination.

UPDATE:There is now a second part to this tutorial talking about more advanced uses of signals. Check it out at Signals - Part 2: Return to sender

Sunday 10 May 2009

Custom Template Filters: Manipulating The Results

After last weeks brief distraction on the topic of inclusion tags, the time has finally come for the tutorial on template filters.


You've probably come across several of the standard filters when writing django templates, they're the things that are appended to variables though the use of the | character. Some common examples include |length which does as it says and returns the length of the variable (I.e. Number of characters if it's a string or number of elements if it's a list or dictionary), |safe which prevents any HTML within the variable being escaped and |date:"D dS M Y" which formats a date type variable using the format string provided.


The first example we're going to be looking at today will be a filter which can be used for sorting a list (by its values) or a dictionary (by its keys). The code is as follows (this should sit in the templatetags directory along with the files for custom template tags):


#File: custom_filters.py
from django import template
from django.utils.datastructures import SortedDict

register = template.Library()

@register.filter(name='sort')
def listsort(value):
if isinstance(value,dict):
new_dict = SortedDict()
key_list = value.keys()
key_list.sort()
for key in key_list:
new_dict[key] = value[key]
return new_dict
elif isinstance(value, list):
new_list = list(value)
new_list.sort()
return new_list
else:
return value
listsort.is_safe = True

If you've been following the tutorials on custom template tags over the last few weeks then most of this should look familiar. The key new points to notice are the decorator @register.filter(name='sort') which tells django that the upcoming function is a filter and that it should go by the name of sort when it is used within a template; SortedDict which is a useful data structure that is provided by django which remembers the order that you added items to it and the line listsort.is_safe = True which gives a hint to django that we've not done anything to the variable to cause it to need escaping (if the variable needed escaping beforehand however then it will still need escaping afterwards).


Pointing a url at the following view should allow you to see the filter in action. Two numbered lists should be displayed, the first being a sorted version of test_list and the second a (key) sorted version of test_dict.


#File: views.py
from django.template import Template,Context
from django.http import HttpResponse
def filter_test(request):
test_list=['Item 1','Item 2','Third', 'Initial element', 'Item 1.5']
test_dict={'a':'First', 't':'Second', 'b':'Third', 'alpha':'Fourth'}
t = Template("""{% load custom_filters %}
<html>
<head><title>Filter Test</title></head>
<body>
<ol>
{% for item in test_list %}
<li>{{ item }}</li>
{% endfor %}
</ol>
<ol>
{% with test_dict|sort as sorted_dict %}
{% for key, value in sorted_dict.items %}
<li>{{ key }} - {{ value }}</li>
{% endfor %}
{% endwith %}
</ol>
</body>
</html>""")
return HttpResponse(t.render(Context({'test_list':test_list,
'test_dict':test_dict,})))

That now shows the basics of creating custom filters, however it's also possible to create them to take extra arguments. To demonstrate this we'll look at a filter which allows you to prefix a string to the front of other string type variables. The code is as follows (append it to the existing file):


#File: custom_filters.py
from django import template
from django.template.defaultfilters import stringfilter

register = template.Library()

@register.filter
@stringfilter
def prefix(value, prefix):
return "%s%s" % (prefix, value)

In this example there's not been any arguments passed in to @register.filter, in this case django will take the name of the function being decorated to be the name of the filter (i.e. prefix). The @stringfilter decorator has been used on the function, which informs django that we only wish to operate on strings and so django will ensure that any variable passed in to the filter has been converted into a string first. Finally we do not specify that the output is safe, as there's no guarantee that the item being prefixed wont require escaping later on and so by not declaring it as safe it will force django to check that the string is escaped later on.


Now alter the view from before as follows (changes highlighted like this) and you should see the same result as before except the elements of test_list will have "Sorted! " prefixed to it.


#File: views.py
from django.template import Template,Context
from django.http import HttpResponse
def filter_test(request):
test_list=['Item 1','Item 2','Third', 'Initial element', 'Item 1.5']
test_dict={'a':'First', 't':'Second', 'b':'Third', 'alpha':'Fourth'}
t = Template("""{% load custom_filters %}
<html>
<head><title>Filter Test</title></head>
<body>
<ol>
{% for item in test_list %}
<li>{{ item|prefix:"Sorted! " }}</li>
{% endfor %}
</ol>
<ol>
{% with test_dict|sort as sorted_dict %}
{% for key, value in sorted_dict.items %}
<li>{{ key }} - {{ value }}</li>
{% endfor %}
{% endwith %}
</ol>
</body>
</html>""")
return HttpResponse(t.render(Context({'test_list':test_list,
'test_dict':test_dict,})))

That's all for the subject of custom filters. Next time we'll take a break from template related tutorials and have a look at Signals in django.

Saturday 2 May 2009

Custom Template Tags - Part 3: A Last Minute Inclusion

After finishing last week's tutorial on block style template tags I realised that there was one template tag creation tip that I had yet to mention and that's the inclusion tag. This is quite closely related to the simple template tags described previously in that it only requires you to write one function. This time however you must provide also provide a template name which will be rendered at the end of it all. Take a look at the following example (a reworking of the simple tag example)


#File: list_formatters.py
from django import template

register = template.Library()

@register.inclusion_tag('format_list_tag.html')
def format_list(input_list):
return { 'unformatted_list':input_list, }

This file is fairly straightforward, we decorate the format_list function to say that it's an inclusion style tag and that it should use the template format_list_tag.html we then return a dictionary which will be used as the context for the template when it is processed. All that's left to include is the following template file:


{# File: format_list_tag.html #}
<ul>
{% for item in unformatted_list %}
<li>{{ item }}</li>
{% endfor %}
<ul>

This file simply provides a snippet of html describing how to display the list. Pop list_formatters.py into your templatetags directory and format_list_tag.html into your templates directory and pointing a url towards the following example (repeated from the previous tutorial) you can see the tag in action.


from django.template import Template,Context
from django.http import HttpResponse
def tag_test(request):
test_list=['Item 1','Item 2','Third time lucky']
t = Template("""{% load list_formatters %}
<html>
<head><title>Tag Test</title></head>
<body>
{% format_list test_list %}
</body>
</html>""")
return HttpResponse(t.render(Context({'test_list':test_list})))

There is one other useful feature of the inclusion tag and that is the ability to access the context. This can be done as follows (changes from the original file have been highlighted):


#File: list_formatters.py
from django import template

register = template.Library()

@register.inclusion_tag('format_list_tag.html', takes_context=True)
def format_list(context):
return { 'unformatted_list':context.get(test_list,[]), }

Whilst this example isn't really the best way to do things it should illustrate the power of what can be done by the inclusion tag and should enable you to use it in your own django sites.


Next time things will be back on track with the promised tutorial on custom filters

Tuesday 28 April 2009

Custom Template Tags - Part 2: Once around the block

In last weeks tutorial we saw how to create simple tags for inclusion in django templates. This week we shall look at how to create block style tags to further to further simplify some common tasks.


Say for example your site design called for a series of articles to be displayed one after another, each one in a box that shared a common style. Such as the (particularly simple) example below:


<div class="content_box">
<h1 class="box_heading">Content Title</h1>
Content
</div>

From what we covered last week it would be possible to create two simple tags, one to represent the opening tags and top of the box, the other to represent the closing tags and the bottom of the box. A cleaner way however would be to create a block style tag, allowing something like the following to be used:


{% content_box "Content Title" %}
Content
{% endcontent_box %}

In order to do this add the following to the file content_tags.py which should be created in the templatetags folder from last week.


from django import template
register = template.Library()

@register.tag(name="content_box")
def do_content_box(parser, token):
try:
tag_name, content_heading = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError, "%r tag requires\
exactly one argument" % token.contents.split()[0]
content_heading = template.Variable(content_heading)
nodelist = parser.parse(('endcontent_box',))
parser.delete_first_token()
return FormatContentBoxNode(content_heading, nodelist)

class FormatContentBoxNode(template.Node):
def __init__(self, content_heading, nodelist):
self.content_heading = content_heading
self.nodelist = nodelist

def render(self, context):
content = self.nodelist.render(context)
return """<div class="content_box">
<h1 class="box_heading">%(title)s</h1>
%(content)s
</div>""" % {'title':self.content_heading,
'content':content,}

This code consists of two parts, the function do_content_box is the main tag code. The parser variable is there to provide access to the main body of the template and the token variable is there to provide access to any variables that may have been passed in to the tag. Variables are extracted from token using split_contents() function. This is a special type of split which ensures that quotes are properly preserved rather than just splitting on any white space going. It should also be noted that the first value returned by split_contents() will always be the name of the tag. The next task is to turn the split parameters into Variable objects. This means that django will save anything encased in quotes as a string, whilst attempting to resolve anything else as a context variable.


The main content is extracted from parser by using the parse(List of tags to stop parsing at) function. This returns a list of nodes that we pass into the second part - the FormatContentBoxNode (more on that shortly however). Once the data you require has been parsed then the parser needs to be told to delete the token it just hit with a call to delete_first_token() otherwise it will attempt to parse it a second time which generally isn't a good idea. Finally we pass our extracted data to a FormatContentBoxNode and return it.


The FormatContentBoxNode is a class that inherits from template.Node and is responsible for saying how the tag should be rendered. The __init__ function is fairly basic just storing whatever it's given for later use. The render function is the important one though as this does all of the hard work. In the example given I've included the snippet of html to be rendered as a string but it could quite easily use a Template that's been rendered to a string (in the same way you might render something in a view).


What is important to note about this function is to make sure that you call render(context) on any node lists that you pass into it. This ensures that django processes all the content within your tags too (so that any variables etc. are expanded correctly and not just output as code). The rest of the function is just simple formatting to prepare the content for output.


Now you can see it in action if you create a new view using the following code and point a url at it:


from django.template import Template,Context
from django.http import HttpResponse
def tag_test(request):
test_list=['Item 1','Item 2','Third time lucky']
t = Template("""{% load content_tags %}
<html>
<head><title>Tag Test 2</title></head>
<body>
{% content_box "Some uniformly formatted content" %}
This text should appear within the div.<br />
Variables can also be used in these tags too such as {{ test_list }}.
{% endcontent_box %}
</body>
</html>""")
return HttpResponse(t.render(Context({'test_list':test_list})))

Now that we've created a basic version of the block style template tag it's time to expand on it to include a second optional tag that delimits the text within. To expand on the first example we will look at how you can add a 'Read More' point in the text entered so that everything below that point will be concealed until the reader clicks to say they wish to read more about it. The code below demonstrates how to do this (all changes from the original example have been highlighted).


@register.tag(name="content_box")
def do_content_box(parser, token):
try:
tag_name, content_heading = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError, "%r tag requires exactly one argument" % token.contents.split()[0]
content_heading = template.Variable(content_heading)
sample_nodelist = parser.parse(('readmore','endcontent_box',))
readmore_nodelist = None
ptoken = parser.next_token()
if ptoken.contents == 'readmore':
readmore_nodelist = parser.parse(('endcontent_box',))
parser.delete_first_token()
return FormatContentBoxNode(content_heading, sample_nodelist, readmore_nodelist)

class FormatContentBoxNode(template.Node):
def __init__(self, content_heading, sample_nodelist, readmore_nodelist):
self.content_heading = content_heading
self.sample_nodelist = sample_nodelist
self.readmore_nodelist = readmore_nodelist

def render(self, context):
sample_content = self.sample_nodelist.render(context)
if self.readmore_nodelist is not None:
further_content = self.readmore_nodelist.render(context)
return """<div class="content_box">
<script type="text/javascript">
function show_readmore() {
document.getElementById('morelink').style.display='none';
document.getElementById('moretext').style.display='block';
return false;
</script>
<h1 class="box_heading">%(title)s</h1>
%(sample_content)s<br />
<a id="morelink" href="#" onclick="return show_readmore();">Read More...</a>
<div id="moretext" style="display:none">%(further_content)s</div>
</div>""" % {'title':self.content_heading,
'sample_content':sample_content,
'further_content':further_content,}
else:

return """<div class="content_box">
<h1 class="box_heading">%(title)s</h1>
%(sample_content)s
</div>""" % {'title':self.content_heading,
'sample_content':sample_content,}

The first change is to the parsing. parse has been given an extra option of 'readmore' the first time through telling it that it should stop if it hits either a 'readmore' or an 'endcontent_box' tag. The second change is that we use next_token() in order to process the token instead of deleting it. This allows us to check what the name of the tag we hit was and if it was the 'readmore' one we can parse the rest of the data until 'endcontent_box' is hit as was done in the first example.


The extra data is then passed through to the FormatContentBoxNode which checks if it was given any readmore text and if so it includes it in a hidden div and includes a read more link to allow it to be made visible (apologies for the quality of the javascript but I wanted something quick and simple to get the point across). In the case where no read more text is included we just display things as before.


Finally this can be tried by putting the code below in a view and pointing a url at it.


from django.template import Template,Context
from django.http import HttpResponse
def tag_test(request):
test_list=['Item 1','Item 2','Third time lucky']
t = Template("""{% load content_tags %}
<html>
<head><title>Tag Test 2</title></head>
<body>
{% content_box "Some uniformly formatted content" %}
This text should appear within the div.<br />
Variables can also be used in these tags too such as {{ test_list }}.
{% readmore %}
Only people who are interested in seeing more will be able to see this
text!
{% endcontent_box %}
</body>
</html>""")
return HttpResponse(t.render(Context({'test_list':test_list})))

That concludes the tutorial on block style template tags. If there's anything here that you would like more advice on, or any topics you would like for a future tutorial then feel free to drop me a comment below. Next time we shall be finishing talking about custom template items by looking at how to create custom filters.

Sunday 19 April 2009

Custom Template Tags - Part 1: Keeping It Simple

If you've ever created a django template before then you'll have used a template tag in one form or another. This tutorial will show you how you can create a simple template tag to help automate a common task.


The simplest of the Simple template tags are used for those cases where you find that you have a chunk of HTML code which is used in multiple places within your code, but because the location isn't static it can't be included in a base template. These tags would take no parameters at all and would output the same chunk of HTML every time. An example of this in the default tags would be {% debug %} which will output a load of debug information at the point the tag is placed.


The next level of complexity for template tags comes by allowing parameters into the simple tags. This could be used for things where you require a consistent format or where you want to run a piece of python code over a variable before displaying it. An example of one if these in the default tags would be {% url url_name %} which we saw last week in the tutorial on naming urls.


Since both of these types of tags are created on a similar way this tutorial will demonstrate how to create one that takes parameters. The example to be used this week will be for a tag that takes a list as a parameter and formats it into a series of bullet points.


The first thing to do is to create a directory named templatetags within your application directory. This is a special directory that django will look in whenever you try to import a set of tags (it will look for this directory in every installed application). In the newly-created directory create a blank file named __init__.py (which is required by python) and a file name list_formatters.py which should contain the following code:


from django import template

register = template.Library()

@register.simple_tag
def format_list(input_list):
return "<ul>\n <li>%s</li>\n<ul>" % "</li>\n <li>".join(input_list)


This code basically registers the function with the template library and says that it's a simple tag. The function then takes its only input and joins it together with some HTML to produce a bullet point style list.


Now we have generated the code for the tag, we can import it into a template and try it out. Create a new view using the following code and point a url at it:


from django.template import Template,Context
from django.http import HttpResponse
def tag_test(request):
test_list=['Item 1','Item 2','Third time lucky']
t = Template("""{% load list_formatters %}
<html>
<head><title>Tag Test</title></head>
<body>
{% format_list test_list %}
</body>
</html>""")
return HttpResponse(t.render(Context({'test_list':test_list})))

View this newly-created page and you should see that the list passed into the context has been formatted as a series of bullet points.


The next tutorial will look at block template tags i.e. ones that take a block of text/code in the middle of them like {% block %}{% endblock %} or {% if condition %}{% else %}{% endif %}

Thursday 16 April 2009

Progress Update

Just a brief update to let you know what the current status of the site is.


I'm in the process of starting up a project on google code for some of the modules and other things I will be working on and talking about in this blog. I hope to get that up there within the next couple of days.


Furthermore this weekend should see the second in my series of django tips where I will be looking at creating your own simple template tags so if you want to know more, watch this space!.

Sunday 12 April 2009

Name your URLs

Today's tip will be delving into the subject of url naming. Whilst this is touched on in part 4 of the django tutorial it is pretty much a blink and you'll miss it affair for what is an incredibly useful tool and thus warrants further consideration.


Take the following snippet of code, extracted from a typical project, as an example:


# File: project/urls.py
from django.conf.urls.defaults import *

urlpatterns = patterns('',
# Include mysite urls
(r'^mysite/', include('mysite.urls')),
)

# File: project/mysite/urls.py
from django.conf.urls.defaults import *
from views import display_homepage, display_about, display_article,\
display_comment

urlpatterns = patterns('',
# Display the homepage.
(r'^$, display_homepage),
# Display the about page.
(r'^about/$', display_about),
# Display an index of all articles.
(r'^article/$', display_article),
# Display a specific article.
(r'^article/(?P<article_id>\d+)/$', display_article),
# Display a specific comment for a specific article.
(r'^article/(?P<article_id>\d+)/comment/(?P<comment_id>\d+)/$',
display_comment),
)

These url files have already been pretty well defined, but they will require urls to be hard coded throughout the views increasing the chance that a link could be malformed and creating a potential logistical nightmare should a url ever have to be changed. This is a perfect time to start naming things!


# File: project/mysite/urls.py
from django.conf.urls.defaults import *

urlpatterns = patterns('mysite.views',
# Display the homepage.
url(r'^$, 'display_homepage', name='mysite_homepage'),
# Display the about page.
url(r'^about/$', 'display_about', name='mysite_about'),
# Display an index of all articles.
url(r'^article/$', 'display_article', name='mysite_article_index'),
# Display a specific article.
url(r'^article/(?P<article_id>\d+)/$',
'display_article', name='mysite_article_by_id'),
# Display all the comments for a specific article.
url(r'^article/(?P<article_id>\d+)/comment/$',
'display_comment', name='mysite_comment_index'),
# Display a specific comment for a specific article.
url(r'^article/(?P<article_id>\d+)/comment/(?P<comment_id>\d+)/$',
'display_comment', name='mysite_comment_by_id'),
)

The first difference that you might notice is that each entry passed in to patterns is now the result of a call to url rather than just being a simple tuple. Internally this doesn't change much as it's pretty much the first thing django does with each entry when it gets them anyway but by doing this yourself it allows you to pass a value in to the name argument. Which leads nicely to the other change made to these files which is the names themselves. Each url entry now has a name which as you can see I've formed from the name of the application and the description of the view.


Now that we've got the names in place we can get around to the good part, ensuring that we no longer have urls hard coded in our project. For inserting urls into templates we can use the template tag aptly named url so where you once had
<a href="/mysite/about/">About This Site</a>
you would now have
<a href="{% url mysite_about %}">About This Site</a>
whilst this may look a little verbose for the simple example given here, if we were to change the url for the about page to about_us/ then one change to the urls.py file would be sufficient to change every occurrence throughout the entire site.


Next we can go about replacing the various instances for the more complex urls, and this is where the url tag really begins to shine as what once would have been
<a href="/mysite/article/{{ article.id }}/comment/{{ comment.id }}/">{{ comment.title }}</a>
becomes
<a href="{% url mysite_comment_by_id article.id comment.id %}/">{{ comment.title }}</a>
By adding parameters one after another this will ensure that they are inserted into the correct place in the url and that if any part of the url changes it will update them all automatically. The only stipulation on any changes is that any parameters must stay in the same order.


Whilst the url tag allows us to eliminate hard coded urls from templates it can't be used within normal python code. For this you need to call upon the reverse function like so:


# File: project/mysite/views.py
from django.core.urlresolvers import reverse
from django.shortcuts import render_to_response

def display_comment_link(request, article_id, comment_id):
"""A function that for some reason passes two urls to a template for display."""
comment_link_url = reverse('mysite_comment_by_id',
kwargs={'article_id':article_id,
'comment_id':comment_id,
}
)
article_link_url = reverse('mysite_article_by_id',
kwargs={'article_id':article_id,}
)
return render_to_response('mysite/display_comment_link.html',
{ 'comment_link_url':comment_link_url,
'article_link_url':article_link_url,
}
)

Now whilst this function is fairly pointless it demonstrates how you can generate the urls programmatically and thus eliminate the need for hard coding throughout your code.


Unfortunately there are still going to be times where you will be unable to use named urls within your code. Luckily these are few and far between and exist solely when the url regular expression contains a choice separated by a | character. In these cases the reverse function will be unable to tell which option should be used and so fail to generate a result. Hopefully the django team will come up with a solution for this issue in the near future, until then if you wish to use naming for those kinds of urls then the regular expression will need to be split up into one entry per combination of options (although this may not be feasible if the expression is particularly complex with many sets of options).


So we have reached the conclusion of this first set of tips on using django. I hope it has been of some use and that going forward your urls will remain manageable. If you have any questions feel free to leave a comment below, and if you are interested in further django tips feel free to register as a follower, or just add the site's feed to your aggregator.

Friday 10 April 2009

So You're Interested In Django?

For those of you that don't know, django is a high level python web framework that can be used to generate potentially complex web sites with a minimal amount of effort.

This blog will be looking at some of the more advanced aspects available to a django programmer in an attempt to reduce the effort required in your site even further.  What this blog wont be featuring however is a guide to starting up a django site, as this information is already freely available on the django project website http://www.djangoproject.com (in particular the tutorial available at http://docs.djangoproject.com/en/dev/intro/tutorial01/ is a good place to start). Although I will be happy to offer any advice to anyone having trouble getting started.