I've had a number of e-mails about how I include the listing of blog tags and archives by month on the side of my website from people who have obviously built up a blog and now want further integration with their website.
Well, it's ultra-simple thanks to a nifty Django feature called template tags. The concept behind template tags is simple - a quick snippet of code in your page templates calls some python code behind the scenes, which does some stuff, and returns either raw HTML code (yuck) or sets new variables in your template context, allowing you to manipulate and display them as you please (cool!)
To get started, in the application directory for your blog, create a directory named templatetags and place an empty file in it, named init.py. Now, create a file - in this case we'll call it blog_months.py. We need to do a few things in this file:
- Import the relevant models so we can access the data
- Create and register new template tag
- Write the function(s) for that tag so they add data to the template's context.
The contents of this file need to be:
from yourproject.blog.models import Tag,Post
from django.template import Library,Node
register = Library()
def build_month_list(parser, token):
"""
{% get_month_list %}
"""
return MonthMenuObject()
class MonthMenuObject(Node):
def render(self, context):
context['blog_months'] = Post.objects.dates("date", "month")
return ''
register.tag('get_month_list', build_month_list)
The important bits of this code are the registration of the tag, and the MonthMenuObject updating context[] and then returning nothing at all.
The context that is set is another Django shortcut - it'll return a date object for each unique month that has a post in it, based on the 'date' column in our Post model. Neat.
Next, create a new file named, for example, blog_tags.py and paste the following into it:
from yourproject.blog.models import Tag,Post
from django.template import Library,Node
register = Library()
def build_tag_list(parser, token):
"""
{% get_tag_list %}
"""
return TagMenuObject()
class TagMenuObject(Node):
def render(self, context):
output = ['']
for blogtag in Tag.objects.all():
number = blogtag.post_set.count()
if number >= 1:
output.append(blogtag)
context['blog_tags'] = output
return ''
register.tag('get_tag_list', build_tag_list)
As you can see, this is very similar to the months list - except it checks to see if each tag has any posts, and if it does it adds that tag to a list. That list is then set in the template context for later use.
And that's the hard bit!
Back in your base.html template, choose where you want your list of months to be, and drop in the following code:
<ul>{% load blog_months %}{% get_month_list %}
{% for month in blog_months %}<li><a href="/blog/{{ month|date:"Y/M"|lower }}/" title="{{ month|date:"M Y" }}">{{ month|date:"M Y" }}</a></li>
{% endfor %}</ul>
That'll load up your .py file, execute the tag registered as get_month_list (which, according to the code above, will set a context variable called blog_months), then run through each month in the list and add a link to your page in an unordered list. We use the standard django template filter named date to format the date for valid use in the links and in the anchor text.
Next, choose where you want your tag list and add this (strikingly similar) block of code to the template:
{% load blog_tags %}{% get_tag_list %}
>ul<
{% for tag in blog_tags %}{% if tag.slug %}>a class="link" href="/tag/{{ tag.slug }}/" title="{{ tag.description|escape }}"<{{ tag.title }}>/a<>/li<
{% endif %}{% endfor %}>/ul<
Just like the month block, that runs your new template tag then turns the Python list into a nicely formatted list of tags.
And that's it - you've now got your blog integrated into your website in areas where the blog isn't actually being loaded via generic views - those links are available anywhere in your site!