Django Tagging

26th June 2008

I confess, I've fallen for the Django framework. It's the most straightforward, hassle free web framework I've used to date and it's the firm foundations of Napes.co.uk. Now I'm sharing the knowledge with fellow n00bs. This tutorial explains how to implement tagging across your project using the much coveted django-tagging framework including tag clouds and filters.

Overview

Getting Started

This tutorial was written using a version of Django which is now deprecated and therefore certain parts of the tutorial may not work with the latest version of the framework.

This article uses the much coveted django-tagging package to implement tagging. You should have it installed already before you begin. By the end of this tutorial you'll be able to:

  • Add and manage tags for models in your project
  • Display inline tag clouds
  • Filter items by tag

The code snippets in this tutorial are based on the code used to host this website and will focus on adding tags to blog articles. However, the same concepts apply to all models and I have successfully implemented them in my photos application as well.

Before starting, make sure you have imported the tagging application in your settings.py file so that INSTALLED_APPS contains 'tagging'.

Models

We begin by modifying the models for the objects we wish to tag. Tags in django-tagging are common to all models and so the same method can be applied to each of the models you want to tag. For simplicity's sake this article only implements tag browsing on a per-model basis.

To add tagging to a model (<appname>/models.py) import the tagging package, add a tag field, and declare a get_tags method as shown below.


from django.db import models
import datetime
from tagging.fields import TagField
from tagging.models import Tag
 
class BlogEntry(models.Model):
    title = models.CharField(max_length=100)
    slug = models.SlugField(unique=True, prepopulate_from=('title',))
    date = models.DateTimeField(default=datetime.datetime.now)
    tags = TagField()
    html = models.BooleanField(default=False)
 
    def get_tags(self):
        return Tag.objects.get_for_object(self) 

Some models will have an existing tag field in which case, you shouldn't add another. TagField() is a tokenized field, in the admin interface it will appear as a single line text field, however, each word will represent a different tag in the database.

You should now run python manage.py syncdb to create the tagging tables in your database. Since tags are held in an external table there is no need to update your model table manually.

Ensure your server is started (python manage.py runserver for the development server) and load your administration control panel (http://localhost:8000/admin/). If you create or edit the model you have been working on you should now see a tags field where you can enter tags for the entry. Try adding some tags to an existing or new item, when you return to the admin homepage you should be able to see them in the Tags collection.

While it's possible to create tags that are capitalise or contain more than one word, for example "New York" this will cause problems when we want to convert to and from slugified versions of the tag (versions that can be used as urls). For this tutorial, I recommend using simple, single word tags using hyphens, for example new-york.

Template Tags

Template tags allow templates to contain information from other applications without having to explicitly define it in the views.py file. Template tags are usually placed in a templatetags directory under the application they are associated with. Since our tags can be associated with any model, I created a new application 'misc' to store any generic applcation files. This is simply a directory under the project directory and a templatetags directory under that. Both should contain a __init__.py file (which can be empty). As with all Django applcations it should also be added to the settings.py file so that INSTALLED_APPS contains 'misc'.

Create a new file inside templatetags called tag_cloud.py. Copy and paste (or create a link to) the contents of django-tagging/tagging/templatetags/tagging_tags.py. This contains the code we will need for creating tag clouds.

As a test, add the following code to your homepage template somewhere it will be visible.


{% load tag_cloud %}
{% tag_cloud_for_model blog.BlogEntry as tags with steps=6 min_count=1 distribution=log %}
{% for tag in tags %}{{tag.name}} ({{tag.font_size}})  {% endfor %}

This will list all the tags in the model blog.BlogEntry along with their 'weighting'. You should change the model to the one you are using in your project in the form <appname>.<ModelName>. In this example, the weighting varies by six degrees, corresponding to the steps=6 declaration.

This is a fairly awful tag cloud, however, we now have the basic fixtures in place.

Views

To ensure that readers can view all the tags for your site, or to allow them to browse items by tags, we will create a set of views specifically for tagging.

First, edit the urls.py file for your application to include the views for the tags as shown below.


from django.conf.urls.defaults import *
 
urlpatterns = patterns('',
 
    (r'^tags/$', 'blog.views.tags'),
    (r'^tag/(?P[-_A-Za-z0-9]+)/$','blog.views.with_tag'),
     (r'^tag/(?P[-_A-Za-z0-9]+)/page/(?Pd+)/$', 'blog.views.with_tag' ),
    )

Note that this does not show all the views for this application and you will likely have several more which you should not remove. As always you should adjust the names of the views to match the name of your application.

Start editing your views.py file for the application you are adding tags to. Import the tagging packages and add two methods, tags and with_tag along side any existing views you may have as shown below.


import django.http as http
import django.shortcuts as shortcuts 
 
from tagging.models import Tag, TaggedItem
import models 
 
def tags(request):
        return shortcuts.render_to_response("blog/tags.html") 
 
def with_tag(request, tag, object_id=None, page=1): 
 
    query_tag = Tag.objects.get(name=tag)
    entries = TaggedItem.objects.get_by_model(models.BlogEntry, query_tag)
    entries = entries.order_by('-date') 
 
    return shortcuts.render_to_response("blog/with_tag.html", dict(tag=tag, entries=entries))  

Be sure to edit the model names and template locations to match those of your application.

Templates

Now that the urls and views are in place we can begin the last, and most rewarding step - creating the templates. We will have three templates to add to our project, inc/tag_cloud.html for showing inline tag clouds, tags.html for showing the complete tag cloud and with_tag.html for showing entries with a specific tag. I have also created a style-sheet to better present the tag clouds.

Note that you will have to surround these templates with valid html for them to work, since most django sites use a base template file I decided not to include all the html cruft. You will also, as always have to edit model and application names to match your project.

Firstly, tags.html:


<h1>Blog Tags</h1>
<div class="tag-cloud">
{% load tag_cloud %}
{% tag_cloud_for_model blog.BlogEntry as tags with steps=6 distribution=log %}
{% for tag in tags %}
<span class="tag-{{tag.font_size|add:"2"}}">
<a href="/blog/tag/{{tag.name|slugify}}/">{{tag.name}}</a>
</span> 
{% endfor %}
</div><!-- tag-cloud end -->

Secondly, with_tag.html:


<h1>Blog Tag Search</h1>
<p>You searched the blog entries for the tag <span class="highlight">{{tag}}</span>.</p>
{% if entries %}
{% for entry in entries %}
<div class="row-{% cycle odd,even %}">
<h2><a href="/blog/{{entry.slug}}/">{{entry.title}}</a></h2>
<h3>{{entry.date|date:"jS F Y"}}</h3>
{{entry.precis|safe|linebreaks}}
{% if entry.content %}
{% endif %}
</div><!-- row end -->
{% endfor %}
{% else %}
<p>No articles have that tag</p>
{% endif %}

Lastly, inc/tag_cloud.html. I have this as a separate file so that I can include it in other pages, such as the blog summary and search pages, and maintain it in a single location. To include files in your templates, use {% include 'blog/inc/tag_cloud.html' %}


<h2>Blog Tags</h2>
<div class="tag-cloud">
{% load tag_cloud %}
{% tag_cloud_for_model blog.BlogEntry as tags with steps=6 min_count=1 distribution=log %}
{% for tag in tags %}
<span class="tag-{{tag.font_size}}"><a href="/blog/tag/{{tag.name|slugify}}/">{{tag.name}}</a></span> 
{% endfor %}
</div><!-- tag-cloud end -->
<p><em>View the <a href="/blog/tags/">whole tag cloud</a></em></p>

On some pages, you may wish to display the tags for a single item rather than the entire collection. This can also be done using the tag_cloud template tag:


<h2>This Article's Tags</h2>
<div class="tag-cloud">
{% load tag_cloud %}
{% tags_for_object entry as tags %}
{% for tag in tags %}
<a href="/blog/tags/{{tag.name|slugify}}/">{{tag.name}}</a> 
{% endfor %}
</div><!-- tag-cloud end --><p>
<em>View the <a href="/blog/tags">whole tag cloud</a></em></p>

As a bonus, here's that style-sheet I was talking about:


.tag-cloud {
    text-align: center;
    border: 1px solid #FFFF00;
    padding: 5px;
    background-color: #FFFFCC;
}
 
.tag-1 {    font-size: 12px;} 
 
.tag-2 {    font-size: 13px;} 
 
.tag-3 {    font-size: 14px;} 
 
.tag-4 {    font-size: 15px;} 
 
.tag-5 {    font-size: 16px;} 
 
.tag-6 {    font-size: 17px;} 
 
/* For large tag cloud 6 step */.tag-7 {    font-size: 18px;} 
 
.tag-8 {    font-size: 19px;}

Conclusion

Hopefully this explains some of the concepts behind django-tagging and gives you a feel to integrate tag clouds and tagging into all the models you want.

By Niall Napier

Back to the blog

comments powered by Disqus

Add to Bookmarks

This Article's Tags

View the whole tag cloud