Login or Sign up

Django-tagging doesn't require documentation

Posted by: skyl on Feb. 10, 2010

You can view the documentation rendered in html here which I didn't know about. There are a ton of features that I don't cover. This article should help you get started though.

Django-tagging is one of the preeminent reusable Django applications. Projects like Pinax allow the "developer" to operate at an incredibly high level, gluing apps together haphazardly. Underneath, django-tagging exposes a powerful, yet still extremely high-level api for the application developer to riff off of. The best place to look in my experience is in the django-tagging source on github. To learn more about the module, open an interactive interpreter like bpython or ipython.

Let's say we have a wee bit of data in this model:

class Attraction(models.Model):
    name = models.CharField(max_length=255)
    # some awesome fields
    tags=TagField()

    def __unicode__(self):
        return self.name

    # basic methods for what tagging provides
    def set_tags(self, tags):
        '''Tags is a space or comma-delimited string'''
        Tag.objects.update_tags(self, tags)

    def get_tags(self):
        return Tag.objects.get_for_object(self)

    # killer methods to come

Let's investigate what django-tagging gives us. Watch out for the the Managers classes that give us super objects on the Tag and TaggedItem classes. You can cheat and skip the console stuff and go straight to the new model class below if you want.

>>> from tagging.models import Tag, TaggedItem
>>> Tag.objects.all()
[<Tag: boonies>, <Tag: city center>, <Tag: durbanville>, <Tag: holy cow>, <Tag: mountain>, <Tag: park>, <Tag: plums>, <Tag: sight-seeing>]
>>> TaggedItem.objects.all()
[<TaggedItem: proteaville attraction [boonies]>, <TaggedItem: proteaville attraction [durbanville]>, <TaggedItem: proteaville attraction [plums]>, <TaggedItem: Devils Peak [city center]>, <TaggedItem: Devils Peak [park]>, <TaggedItem: park and plums [park]>, <TaggedItem: park and plums [plums]>, <TaggedItem: Devils Peak [mountain]>, <TaggedItem: Devils Peak [sight-seeing]>, <TaggedItem: Devils Peak [holy cow]>]
>>> from attractions.models import Attraction
>>> for a in Attraction.objects.all():
>>>     print a, ':', a.tags
>>>
>>>
proteaville attraction : boonies durbanville plums
Devils Peak : city center, holy cow, mountain, park, sight-seeing
park and plums : park plums
>>> TaggedItem.objects.get_by_model(Attraction, 'plums') # get all of the attractions with this tag
[<Attraction: proteaville attraction>, <Attraction: park and plums>]
>>> TaggedItem.objects.get_by_model(Attraction, ['plums', 'park']) # Attractions with both of these tags
[<Attraction: park and plums>]
>>> TaggedItem.objects.get_by_model(Attraction.objects.filter(name='park and plums'), 'plums') # Use a queryset
[<Attraction: park and plums>]
>>> TaggedItem.objects.get_union_by_model(Attraction, ['plums', 'park']) # Attraction object with either tag.
[<Attraction: proteaville attraction>, <Attraction: Devils Peak>, <Attraction: park and plums>]
>>> a=Attraction.objects.all()[1]
>>> a # an Attraction object
<Attraction: Devils Peak>
>>> a.tags # comma-delimited string for tags
u'city center, holy cow, mountain, park, sight-seeing'
>>> a.get_tags() # we made a method to get the queryset of tags
[<Tag: city center>, <Tag: holy cow>, <Tag: mountain>, <Tag: park>, <Tag: sight-seeing>]
>>> TaggedItem.objects.get_related(a, Attraction) # Devil's peak is related to park and plums by the park tag
[<Attraction: park and plums>]
>>> b = TaggedItem.objects.get_related(a, Attraction)[0] # let's get that Attraction object
>>> b
<Attraction: park and plums>
>>> TaggedItem.objects.get_related(b, Attraction) # related to Devil's peak by park and proteadville attraction by plums
[<Attraction: Devils Peak>, <Attraction: proteaville attraction>]
>>> TaggedItem.objects.get_related(b, Attraction, num=1) # we can limit to an arbitrary number
[<Attraction: Devils Peak>]
>>> # As with most of these sweet methods, we could be using a queryset instead of a model:
>>> TaggedItem.objects.get_related(b, Attraction.objects.filter(name='proteaville attraction'))
[<Attraction: proteaville attraction>]
>>> Tag.objects.update_tags(a, '"city center" park mountain sight-seeing') # set a new tag string for a
>>> a=Attraction.objects.all()[1] # but we have to get it from the database or it is not updated
>>> a.tags
u'city center, mountain, park, sight-seeing'
>>> Tag.objects.add_tag(a, "'holy cow'") # This doesn't work, we need double quotes
Traceback ...
>>> Tag.objects.add_tag(a, '"holy cow"') # add the two word tag to "a"s tags
>>> a.tags # not there in our python object
u'city center, mountain, park, sight-seeing'
>>> a = Attraction.objects.all()[1] # we must get a new result from the database
>>> a.tags # now "holy cow" is in there
u'city center, holy cow, mountain, park, sight-seeing'
>>> Tag.objects.get_for_object(a) # we can get the Tags for an object
[<Tag: city center>, <Tag: holy cow>, <Tag: mountain>, <Tag: park>, <Tag: sight-seeing>]
>>> Tag.objects.usage_for_model(Attraction) # we can get all of the tags attached to a model (or queryset)
[<Tag: boonies>, <Tag: city center>, <Tag: durbanville>, <Tag: holy cow>, <Tag: mountain>, <Tag: park>, <Tag: plums>, <Tag: sight-seeing>]
>>> t=Tag.objects.usage_for_model(Attraction)[0] # we get a tag object from the tag list/queryset
>>> t.count # it does not have a count attr
>>> t=Tag.objects.usage_for_model(Attraction, counts=True)[0] # we can set counts=True and grab a Tag object
>>> t.count # and the tag members will have a count attribute, the number of objects of this type tagged with this Tag
1L
>>> t=Tag.objects.usage_for_model(Attraction, counts=True)[6] # 'plums' is the sixth index
>>> t.count # 'plums' is tagged to 2 Attractions
2L
>>> t.name
u'plums'
>>> Tag.objects.usage_for_model(Attraction, min_count=2) # get only the tags that are tagged min_count or greater times
[<Tag: park>, <Tag: plums>]
>>> a.name
u'Devils Peak'
>>> Tag.objects.usage_for_model(Attraction, filters={'name':'Devils Peak'})
[<Tag: city center>, <Tag: holy cow>, <Tag: mountain>, <Tag: park>, <Tag: sight-seeing>]
>>> Tag.objects.usage_for_model(Attraction, filters={'name__in':['Devils Peak','park and plums']})
[<Tag: city center>, <Tag: holy cow>, <Tag: mountain>, <Tag: park>, <Tag: plums>, <Tag: sight-seeing>]
>>> Tag.objects.related_for_model('park', Attraction, counts=True) # Tags that appear in Attraction model alongside 'park'
[<Tag: city center>, <Tag: holy cow>, <Tag: mountain>, <Tag: plums>, <Tag: sight-seeing>]
>>> Tag.objects.related_for_model('plums', Attraction, counts=True)
[<Tag: boonies>, <Tag: durbanville>, <Tag: park>]
>>> a.tags
u'city center, holy cow, mountain, park, sight-seeing'
>>> Tag.objects.related_for_model('mountain park', Attraction, counts=True)
[<Tag: city center>, <Tag: holy cow>, <Tag: sight-seeing>]
>>> def show(cloud):
...     for t in cloud:
...         print t.name, t.count, t.font_size
...
>>> cloud = Tag.objects.cloud_for_model(Attraction)
>>> show(cloud)
boonies 1 1
city center 1 1
durbanville 1 1
holy cow 1 1
mountain 1 1
park 2 4
plums 2 4
sight-seeing 1 1
>>> for a in Attraction.objects.all():Tag.objects.add_tag(a, 'durbanville')  # add durbanville to all Attractions
>>> cloud = Tag.objects.cloud_for_model(Attraction)
>>> show(cloud)
boonies 1 1
city center 1 1
durbanville 3 4
holy cow 1 1
mountain 1 1
park 2 2
plums 2 2
sight-seeing 1 1
>>> from tagging.utils import LINEAR
>>> cloud = Tag.objects.cloud_for_model(Attraction, steps=8, distribution=LINEAR)
>>> show(cloud)
boonies 1 1
city center 1 1
durbanville 3 8
holy cow 1 1
mountain 1 1
park 2 4
plums 2 4
sight-seeing 1 1
>>> # we can get a cloud for any arbitrary queryset of objects with a TagField
>>> cloud = Tag.objects.cloud_for_model(Attraction, filters={'name__in':['park and plums', 'proteaville attraction']})
>>> show(cloud)
boonies 1 1
durbanville 2 4
park 1 1
plums 2 4

So, let's integrate these capabilities as methods on our model.

from django.contrib.gis.db import models
from tagging.models import Tag, TaggedItem
from tagging.fields import TagField

from django.template import Template, Context
tag_cloud_template = Template(
"""{% for t in tags %}<span class="tag-{{t.font_size}}">{{ t.name }}</span>{{ t.count }}{% endfor %}"""
)

def render_node(template, dictionary):
    return template.render(Context(dictionary))


class Attraction(models.Model):
    name = models.CharField(max_length=255)
    tags=TagField()

    def __unicode__(self):
        return self.name

    # TAGS
    def set_tags(self, tags):
        Tag.objects.update_tags(self, tags)

    def get_tags(self):
        return Tag.objects.get_for_object(self)

    def add_tag(self, tag):
        Tag.objects.add_tag(self, tag)

    def related_tags(self):
        '''Tags of items that have all of the Tags'''
        return Tag.objects.related_for_model(self.tags, self.__class__, counts=True)

    def get_cloud(self):
        '''The raw list of tags with count and font_size attributes'''
        return Tag.objects.cloud_for_model(self.__class__)

    def render_cloud(self):
        '''Produce the html for the cloud'''
        context = { 'tags': self.get_cloud(), }
        return render_node(tag_cloud_template, context)

    def related_attractions(self, num=None):
        '''Instances of this model that have a tag in common'''
        return TaggedItem.objects.get_related(self, self.__class__, num=num)

    def attractions_common_tag(self):
        '''Same as related_attractions but with a different method'''
        return TaggedItem.objects.get_union_by_model(
            self.__class__.objects.exclude(slug=self.slug),
            self.tags
        )
    # See also  get_by_model, get_intersection_by_model on the TaggedItem.objects.

Comments on This Post:

Please Login (or Sign Up) to leave a comment