Adding Archives Information Via Django Templatetag

Written by Martin Geber on October 28, 2007 at 6:18 p.m. Filed unter: DjangoUsabilityWeb standards.  2 Comments. Trackback URL.

Contents

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:

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:

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:

Firefox Extension: Link Widget

You see that you have (when you click at the Folder-Button) information on archives and on the author.

Now, after you learned how to add these <link>-tags I hope it is clear how to use them practical.

Comments

The comments also include all Trackbacks.

#1

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.

 
#2

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

 
Post a Comment


Or

Your Way: Home » Thoughts » 2007 » October » Sunday, 28 » Adding Archives Information Via Django Templatetag