It is quite typical for Wordpress-blogs to have all archives in the sidebar. For example "August 2005, September 2005", all linked back to the archives pages. Django, sadly, doesn't have something by default to generate these archive links dynamically.
There is another reason, why people, should want this ability, even though they don't want these links in their sidebar: the <link rel="archives" />-tags. In case you're not interested in this HTML-tag, just ignore the following paragraphs and skip directly to the source code.
<link rel="archives" />-HTML-Tag
I love one special little HTML-Tag, which isn't yet supported by any of the usual browsers: the <link>-tag. There is one great relation (rel), which provides the information about all archives the current website hosts. As Django doesn't provide something like that, I wrote a templatetag, which adds the <link rel="archives" />-tag. (I'm not interested in the sidebar at all, but my tag should have the ability, to do this job too.)
How the <link>-tag works
The <link>-tag takes usually three attributes:
- rel: The relation of the link. For example archive, which means this link holds an overview of contents.
- href: The actual link, which usually is provided relatively.
- title: Make the Link human-readable. For example Archive of August 2007.
Notice: The <link>-tag belongs into the <head>-tag.
Basics about <link rel="archives" />
Like said above it enables you to tell the browser, user and of course search engines, where your archives are located. So when someone enters your website, s/he directly sees all important overview pages at once.
Which other types of rel="" are available?
There are various other relations of this tag available, here is a list of the most important:
- author: Link to the author. <link rel="author" href="/about/" title="Martin Geber" />
- prev: Link to the content-page, which logically was before the current content-page, for example the photo, which was posted before the current one.
- next: Link to the content-page, which was posted after the current one, analogue to the prev-relation.
- up: Link to the content-level above. Let's stay with the photo-example. You are currently browsing one photo, up would than mean to jump to the album, the photo belongs to.
- start: Link to the home page, to let users jump directly to the start. <link rel="start" href="/" title="Homepage" />
I guess there are even more, but these are the most important to provide a great browsing experience for the user. (See the last chapter for more information on that.)
The Django Template Tag to create archives
Basically it is possible to provide archives of all your contents, photos, blogentries, or bookmarks. So the templatetag must be very flexible. Hopefully it really is.
If you know, how to set-up a template tag in Django, skip to the next headline). Otherwise, do as follows: Create a folder called templatetags into an existing application, or start a new application, which you have to include into your INSTALLED_APPS-variable in the settings.py. Create a new file there, e.g. taglib.py. That's it.
Source code of the Archives-templatetag
This is the Python source code of the template tag, which will be registered to get_archives:
from django.template import Library, Node, TemplateSyntaxError
from django.db.models import get_model
from django.db import connection
register = Library()
class ContentArchives(Node):
"""
Retrieve an monthly archive listing, based on the a model of your
decision. Of course you can format the output completely.
When you use HTML as format please note that you should use single
quotes ``'`` around the format instead the usual double quote ``"``.
The format string can take these "variables", used like in Python
(``%(variable)s``):
* ``nicedate`` -- SQL-DateFormat string "%M %Y",
e.g. "August 2007".
* ``name`` -- The plural verbose name, which was defined in
the ``Meta`` class by the attribute ``verbose_name_plural``.
If you use i18n, this will also be translated automaticly.
* ``year`` -- Year of the archive, four digits.
* ``month`` -- Month of the archive, two digits.
* ``count`` -- Number of nodes in the current archive.
You should really use ``name`` as variable, especially when you use
this template tag for more than one model. Otherwise, it is
possible that you have "August 2005" twice as title...
Syntax::
{% get_archives `object] `datefield] with `urlformat] %}
Example::
{% get_archives weblog.Entry pub_date with '<link rel="archives" title="%(name)s %(nicedate)s (%(count)s)" href="/weblog/%(year)s/%(month)s/" />' %}
Example Output::
<link rel="archives" title="Blog Entries August 2007 (1)" href="/weblog/2007/08/" />
<link rel="archives" title="Blog Entries November 2006 (1)" href="/weblog/2006/11/" />
<link rel="archives" title="Blog Entries July 2006 (1)" href="/weblog/2006/07/" />
*Please Note:* ``%(name)s`` will be translated, in case you use I18N.
"""
def __init__(self, model, field, htmlformat):
self.field, self.htmlformat = field, htmlformat
self.model_table = model.replace('.', '_').lower()
self.model = get_model(*model.split('.'))
def render(self, context):
cursor = connection.cursor()
try:
from MySQLdb import OperationalError
except ImportError: pass
try:
cursor.execute('SELECT DATE_FORMAT(' + self.field +
', "%M %Y") AS date_title, DATE_FORMAT(' +
self.field + ', "%Y") AS year, DATE_FORMAT(' +
self.field + ', "%m") AS month,' +
'COUNT(*) as num FROM ' + self.model_table +
' GROUP BY date_title ORDER BY year DESC, month DESC')
except OperationalError:
raise TemplateSyntaxError('get_archives tag seems to have gotten wrong arguments. '+
'does the table "%s" has a field "%s"?' % (self.model_table, self.field))
archives = cursor.fetchall()
html_archive_links = ''
for archive in archives:
html_archive_links += self.htmlformat % {'nicedate': archive[0],
'year': archive[1],
'month': archive[2],
'count': archive[3],
'name': self.model._meta.verbose_name_plural}
return html_archive_links
def get_archives(parser, token):
bits = token.split_contents()
if len(bits) != 5:
raise TemplateSyntaxError('get_archives tag takes exactly four arguments')
if bits[3] != 'with':
raise TemplateSyntaxError('third argument to get_archives tag must be "with"')
return ContentArchives(bits[1], bits[2], bits[4][1:-1]+"\n\t")
get_archives = register.tag(get_archives)
Usage of the Archives-templatetag
Like written in the Docstring of the ContentArchives-class, you can define the object, you want to use as archives. It must have a date-field, which will be given as second parameter to the template tag. The third parameter, or fourth if you count with as parameter, is the format you want to print the archives:
{% get_archives weblog.Entry pub_date with '<link rel="archives" title="%(name)s %(nicedate)s (%(count)s)" href="/weblog/%(year)s/%(month)s/" />' %}
You see that you are able to either use the described <link rel="archives" />-tag or any other link, often used in the sidebar. This would look like something like this:
{% get_archives weblog.Entry pub_date with '<a href="/weblog/%(year)s/%(month)s/" title="%(name)s %(nicedate)s (%(count)s)">%(nicedate)s</a>' %}
One last note on the <link>-tag
Firefox doesn't have by default the ability to make use of the <link>-tags, but you can give it a little help by using this extension: Link Widgets. Here is a screen shot of it in action:
You see that you have (when you click at the Folder-Button) information on archives and on the author.
- The right and left button use the <link rel="next" />-tag and <link rel="prev" />-tag.
- The arrow up (without line above it) jumps one level upwards, using the <link rel="up" />-tag. I decided that upwards mean the day the article was published.
- The arrow up with a line above it is the start of the website defined by <link rel="start" />.
Now, after you learned how to add these <link>-tags I hope it is clear how to use them practical.


Comments
Kevin commented, on December 14, 2007 at 4:05 a.m.:
Don't forget to also make an __init__.py file (it can be blank) in the templatetags directory. Otherwise, Python is not likely to find your "taglib" module.
Martin commented, on December 17, 2007 at 12:51 p.m.:
Hey Kevin,
yes, I forgot to mention it... I hope everybody knows that.
Cheers