==============
Django Tagging
==============

A generic tagging application for `Django`_ projects, which allows
association of a number of tags with any ``Model`` instance and makes
retrieval of tags simple.

.. _`Django`: http://www.djangoproject.com


Installation
============

Downloading django-tagging
--------------------------

There are two options for downloading; one is to download the latest
packaged version from http://code.google.com/p/django-tagging/ and
unpack it; inside is a script called setup.py. Type this command::

    python setup.py install

...and the package will install automatically.

The other method is to perform a Subversion checkout from somewhere on
your Python path:

    svn checkout http://django-tagging.googlecode.com/svn/trunk/tagging/

Keep in mind that the current code in SVN may be different from the
packaged release, and may contain bugs and backwards-incompatible
changes.

Using django-tagging in your projects
-------------------------------------

Once you've downloaded django-tagging and want to use it in your
projects, do the following::

    1. Put ``'tagging'`` in your ``INSTALLED_APPS`` setting.
    2. Run the command ``manage.py syncdb``.

The ``syncdb`` command creates the necessary database tables and
creates permission objects for all installed apps that need them.

That's it!


Tags
====

Tags are represented by the ``Tag`` model, which lives in the
``tagging.models`` module.

API reference
-------------

Fields
~~~~~~

``Tag`` objects have the following fields:

    * ``name`` -- The name of the tag. This is a unique value
      consisting only of unicode alphanumeric characters, numbers,
      underscores and hyphens.

Manager functions
~~~~~~~~~~~~~~~~~

The ``Tag`` model has a custom manager which has the following helper
functions:

    * ``update_tags(obj, tag_names)`` -- Updates tags associated with
      an object.

      ``tag_names`` is a string containing tag names with which
      ``obj`` should be tagged. Valid tag names may contain unicode
      alphanumeric characters, numbers, underscores or hyphens.
      Multiple tag names may be specified, separated by any number of
      commas and spaces.

      If ``tag_names`` is ``None`` or ``''``, the object's tags will
      be cleared.

    * ``get_for_object(obj)`` -- Returns a ``QuerySet`` containing all
      ``Tag`` objects associated with ``obj``.

    * ``usage_for_model(Model, counts=False, min_count=None, filters=None)``
      -- Returns a list of ``Tag`` objects associated with instances
      of ``Model``.

      If ``counts`` is ``True``, a ``count`` attribute will be added
      to each tag, indicating how many times it has been associated
      with instances of ``Model``.

      If ``min_count`` is given, only tags which have a ``count``
      greater than or equal to ``min_count`` will be returned. Passing
      a value for ``min_count`` implies ``counts=True``.

      To limit the tags (and counts, if specified) returned to those
      used by a subset of the model's instances, pass a dictionary of
      field lookups to be applied to ``Model`` as the ``filters``
      argument.

    * ``related_for_model(tags, Model, counts=False, min_count=None)``
      -- Returns a list of tags related to a given list of tags - that
      is, other tags used by items which have all the given tags.

      If ``counts`` is ``True``, a ``count`` attribute will be added
      to each tag, indicating the number of items which have it in
      addition to the given list of tags.

      If ``min_count`` is given, only tags which have a ``count``
      greater than or equal to ``min_count`` will be returned. Passing
      a value for ``min_count`` implies ``counts=True``.

    * ``cloud_for_model(Model, steps=4, distribution=LOGARITHMIC,
      filters=None, min_count=None)`` -- Returns a list of the
      distinct ``Tag`` objects associated with instances of ``Model``,
      each having a ``count`` attribute as above and an additional
      ``font_size`` attribute, for use in creation of a tag cloud (a
      type of weighted list).

      ``steps`` defines the number of font sizes available -
      ``font_size`` may be an integer between ``1`` and ``steps``,
      inclusive.

      ``distribution`` defines the type of font size distribution
      algorithm which will be used - logarithmic or linear. It must
      be either ``tagging.utils.LOGARITHMIC`` or
      ``tagging.utils.LINEAR``.

      To limit the tags displayed in the cloud to those associated
      with a subset of the Model's instances, pass a dictionary of
      field lookups to be applied to the given Model as the
      ``filters`` argument.

      To limit the tags displayed in the cloud to those with a
      ``count`` greater than or equal to ``min_count``, pass a value
      for the ``min_count`` argument.

Basic usage
-----------

Tagging objects and retrieving an object's tags
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Objects may be tagged using the ``update_tags`` helper function::

    >>> from shop.apps.products.models import Widget
    >>> from tagging.models import Tag
    >>> widget = Widget.objects.get(pk=1)
    >>> Tag.objects.update_tags(widget, 'house thing')

Retrieve tags for an object using the ``get_for_object`` helper
function::

    >>> Tag.objects.get_for_object(widget)
    [<Tag: house>, <Tag: thing>]

Tags are created, associated and unassociated accordingly when you use
``update_tags``::

    >>> Tag.objects.update_tags(widget, 'house monkey')
    >>> Tag.objects.get_for_object(widget)
    [<Tag: house>, <Tag: monkey>]

Clear an object's tags by passing ``None`` or ``''`` to
``update_tags``::

    >>> Tag.objects.update_tags(widget, None)
    >>> Tag.objects.get_for_object(widget)
    []

Retrieving tags used by a particular model
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

To retrieve all tags used for a particular model, use the
``get_for_model`` helper function::

    >>> widget1 = Widget.objects.get(pk=1)
    >>> Tag.objects.update_tags(widget1, 'house thing')
    >>> widget2 = Widget.objects.get(pk=2)
    >>> Tag.objects.update_tags(widget2, 'cheese toast house')
    >>> Tag.objects.get_for_model(Widget)
    [<Tag: cheese>, <Tag: house>, <Tag: thing>, <Tag: toast>]

To get a count of how many times each tag was used for a particular
model, pass in ``True`` for the ``counts`` argument::

    >>> tags = Tag.objects.usage_for_model(Widget, counts=True)
    >>> [(tag.name, tag.count) for tag in tags]
    [('cheese', 1), ('house', 2), ('thing', 1), ('toast', 1)]

To get counts and limit the tags returned to those with counts above a
certain size, pass in a ``min_count`` argument::

    >>> tags = Tag.objects.usage_for_model(Widget, min_count=2)
    >>> [(tag.name, tag.count) for tag in tags]
    [('house', 2)]

You can also specify a dictionary of `field lookups`_ to be used to
restrict the tags and counts returned based on a subset of the
model's instances. For example, the following would retrieve all tags
used on Widgets created by a user named Alan which have a size
greater than 99::

    >>> Tag.objects.usage_for_model(Widget, filters=dict(size__gt=99, user__username='Alan'))

.. _`field lookups`: http://www.djangoproject.com/documentation/db-api/#field-lookups


Tagged Items
============

The relationship between a ``Tag`` and an object is represented by
the ``TaggedItem`` model, which lives in the ``tagging.models``
module.

API reference
-------------

Fields
~~~~~~

``TaggedItem`` objects have the following fields:

    * ``tag`` -- The ``Tag`` an object is associated with.
    * ``content_type`` -- The ContentType of the associated object.
    * ``object_id`` -- The id of the associated object.
    * ``object`` -- The associated object.

Manager functions
~~~~~~~~~~~~~~~~~

The ``TaggedItem`` model has a custom manager which has the following
helper functions:

    * ``get_by_model(Model, tag)`` -- If ``tag`` is an instance of a
      ``Tag``, returns a ``QuerySet`` containing all instances of
      ``Model`` which are tagged with it.

      If ``tag`` is a list of tags, returns a ``QuerySet`` containing
      all instances of ``Model`` which are tagged with every tag in
      the list.

    * ``get_intersection_by_model(Model, tags)`` -- Returns a
      ``QuerySet`` containing all instances of ``Model`` which are
      tagged with every tag in the list.

      ``get_by_model`` will call this function behind the scenes when
      you pass it a list, so it's recommended that you use
      ``get_by_model`` instead of calling this function directly.

    * ``get_related(obj, Model, num=None)`` - Returns instances of
       ``Model`` which share tags with the model instance ``obj``,
       ordered by the number of shared tags in descending order.

       If ``num`` is given, a maximum of ``num`` instances will be
       returned.

Basic usage
-----------

Retrieving tagged objects
~~~~~~~~~~~~~~~~~~~~~~~~~

Objects may be retrieved based on their tags using the ``get_by_model``
helper function::

    >>> from shop.apps.products.models import Widget
    >>> from tagging.models import Tag
    >>> house_tag = Tag.objects.get(name='house')
    >>> TaggedItem.objects.get_by_model(Widget, house_tag)
    [<Widget: pk=1>, <Widget: pk=2>]

Passing a list of tags to ``get_by_model`` returns an intersection of
objects which have those tags, i.e. tag1 AND tag2 ... AND tagN::

    >>> thing_tag = Tag.objects.get(name='thing')
    >>> TaggedItem.objects.get_by_model(Widget, [house_tag, thing_tag])
    [<Widget: pk=1>]

Functions which take tags are flexible when it comes to tag input::

    >>> TaggedItem.objects.get_by_model(Widget, Tag.objects.filter(name__in=['house', 'thing']))
    [<Widget: pk=1>]
    >>> TaggedItem.objects.get_by_model(Widget, 'house thing')
    [<Widget: pk=1>]
    >>> TaggedItem.objects.get_by_model(Widget, ['house', 'thing'])
    [<Widget: pk=1>]


Utilities
=========

Tag-related utility functions are defined in the ``tagging.utils``
module:

get_tag_name_list(tags_names)
-----------------------------

Finds tag names in the given string and return them in a list.

get_tag_list(tags)
------------------

Utility function for accepting tag input in a flexible manner.

If a ``Tag`` object is given, it will be returned in a list as its
single occupant.

If given, the tag names in the following will be used to create a
``Tag`` ``QuerySet``:

    * A string, which may contain multiple tag names.
    * A list or tuple of strings corresponding to tag names.
    * A list or tuple of integers corresponding to tag ids.

If given, the following will be returned as-is:

    * A list or tuple of ``Tag`` objects.
    * A ``Tag`` ``QuerySet``.

calculate_cloud(tags, steps=4, distribution=tagging.utils.LOGARITHMIC)
----------------------------------------------------------------------

Adds a ``font_size`` attribute to each tag given according to the
frequency of its use, as indicated by its ``count`` attribute.

``steps`` defines the range of font sizes - ``font_size`` will be an
integer between 1 and ``steps`` (inclusive).

``distribution`` defines the type of font size distribution algorithm
which will be used - logarithmic or linear. It must be either
``tagging.utils.LOGARITHMIC`` (default) or ``tagging.utils.LINEAR``.

The algorithm to scale the tags logarithmically is from a blog post by
Anders Pearson, `Scaling tag clouds`_.

.. _`Scaling tag clouds`: http://thraxil.com/users/anders/posts/2005/12/13/scaling-tag-clouds/


Model Fields
============

The ``tagging.fields`` module contains fields which make it easy to
integrate tagging into your models and into the
``django.contrib.admin`` application.

Field types
-----------

``TagField``
~~~~~~~~~~~~

A ``CharField`` that actually works as a relationship to tags "under
the hood".

Using this example model::

    class Link(models.Model):
        ...
        tags = TagField()

Setting tags::

    >>> l = Link.objects.get(...)
    >>> l.tags = 'tag1 tag2 tag3'

Getting tags for an instance::

    >>> l.tags
    'tag1 tag2 tag3'

Getting tags for a model - i.e. all tags used by all instances of the
model::

    >>> Link.tags
    'tag1 tag2 tag3 tag4 tag5'

This field will also validate that it has been given a valid list of
tag names, separated by a single comma, a single space or a comma
followed by a space, using the ``isTagList`` validator from
``tagging.validators``.


Form Fields
===========

The ``tagging.forms`` module contains a ``Field`` for use with
Django's `newforms library`_ which takes care of validating tag name
input when used in your forms.

.. _`newforms library`: http://www.djangoproject.com/documentation/newforms/

Field types
-----------

``TagField``
~~~~~~~~~~~~

A form ``Field`` which is displayed as a single-line text input, which
validates that the input it receives is a valid list of tag names,
separated by a single comma, a single space or a comma followed by a
space.

When you generate a form for one of your models automatically, using
the ``form_for_model`` or ``form_for_instance`` functions provided by
the newforms library, any ``tagging.fields.TagField`` fields in your
model will automatically be represented by a
``tagging.forms.TagField`` in the generated form.


Simplified tagging and retrieval of tags with properties
========================================================

If you're not using ``TagField``, a useful method for simplifying
tagging and retrieval of tags for your models is to set up a
property::

    from django.db import models
    from tagging.models import Tag

    class MyModel(models.Model):
        name = models.CharField(maxlength=100)
        tag_list = models.CharField(maxlength=255)

        def save(self):
            super(MyModel, self).save()
            self.tags = self.tag_list

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

        def _set_tags(self, tag_list):
            Tag.objects.update_tags(self, tag_list)

        tags = property(_get_tags, _set_tags)

        def __str__(self):
            return self.name

Once you've set this up, you can access and set tags in a fairly
natural way::

    >>> obj = MyModel.objects.get(pk=1)
    >>> obj.tags = 'foo bar'
    >>> obj.tags
    [<Tag: bar>, <Tag: foo>]

Remember that ``obj.tags`` will return a ``QuerySet``, so you can
perform further filtering on it, should you need to.


Template tags
=============

The ``tagging.templatetags.tagging_tags`` module defines a number of
template tags which may be used to work with tags.

Tag reference
-------------

tags_for_model
~~~~~~~~~~~~~~

Retrieves a list of tags associated with the given model and stores
them in a context variable.

The model is specified in ``[appname].[modelname]`` format.

If specified - by providing extra ``with counts`` arguments - adds a
``count`` attribute to each tag containing the number of instances of
the given model which have been tagged with it.

Example usage::

    {% tags_for_model products.Widget as widget_tags %}

    {% tags_for_model products.Widget as widget_tags with counts %}

tags_for_object
~~~~~~~~~~~~~~~

Retrieves a list of tags associated with an object and stores them in
a context variable.

Example usage::

    {% tags_for_object widget as tag_list %}

tagged_objects
~~~~~~~~~~~~~~

Retrieves a list of objects for a given model which are tagged with
a given tag and stores them in a context variable.

The tag must be an instance of a ``Tag``, not the name of a tag.

The model is specified in ``[appname].[modelname]`` format.

Example usage::

    {% tagged_objects house_tag in products.Widget as widgets %}