===========
Django MPTT
===========

Utilities for implementing Modified Preorder Tree Traversal (MPTT) with
your `Django`_ ``Model`` classes and working with trees of ``Model``
instances.

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

.. contents::

What is Modified Preorder Tree Traversal?
=========================================

MPTT is a technique for storing hierarchical data in a database in a
manner which makes tree retrieval operations such as fetching complete
trees, item ancestors and item descendants very efficient.

The trade-off for this efficiency is that performing inserts and moving
items around is more involved, as there's some extra work required to
keep the tree structure in a good state at all times.

Here are a couple of articles about MPTT to whet your appetite:

    * `Storing Hierarchical Data in a Database`_
    * `Managing Hierarchical Data in MySQL`_

.. _`Storing Hierarchical Data in a Database`: http://www.sitepoint.com/print/hierarchical-data-database
.. _`Managing Hierarchical Data in MySQL`: http://dev.mysql.com/tech-resources/articles/hierarchical-data.html


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

Downloading
-----------

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

   python setup.py install

...and the package will install automatically.

Alternatively, the application's source code can be obtained by
performing a `Subversion`_ checkout. For example, the following command
will check the application's source code out to an ``mptt`` directory::

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

Add the resulting folder to your `PYTHONPATH`_ or symlink (`junction`_,
if you're on Windows) the ``mptt`` directory *inside* it into a
directory which is on your PYTHONPATH, such as your Python
installation's ``site-packages`` directory.

You can verify that the application is available on your PYTHONPATH by
opening a Python interpreter and entering the following commands::

   >>> import mptt
   >>> mptt.VERSION
   (0, 1, None)

.. _`Subversion`: http://subversion.tigris.org
.. _`PYTHONPATH`: http://docs.python.org/tut/node8.html#SECTION008110000000000000000
.. _`junction`: http://www.microsoft.com/technet/sysinternals/FileAndDisk/Junction.mspx

Using Django MPTT in your projects
----------------------------------

Once you've downloaded Django MPTT and want to use it in your
projects, do the following:

   1. Put ``'mptt'`` in your ``INSTALLED_APPS`` setting.

That's it!


Django MPTT feature overview
============================

* A tree structure is automatically managed when you create or delete
  MPTT ``Model`` instances. New instances are added as the last child of
  their parent if they have one, otherwise they become the root of a new
  tree.

* MPTT ``Model`` instances have methods for changing their position in
  the tree, retrieving their ancestors, siblings and descendants from
  the database and determining the number of descendants they have
  (which does not involve a database hit with MPTT).

* The tree structure is automatically updated when you change a
  ``Model`` instance's parent - modified instances are moved so they are
  the last child of their new parent.

  This allows basic management of the tree using the parent field
  available to you in the ``django.contrib.admin`` application and in
  ``newforms`` forms generated using the ``ModelForm`` class.

* The custom ``TreeManager`` manager which is added to all MPTT
  ``Model`` classes provides tree management operations which can be
  used to move any node in the tree (and its descendants) to an
  arbitrary point in the tree.

* Utilities for working with tree items in templates.


Setting up a ``Model`` class for MPTT
=====================================

The ``mptt.models`` module contains a ``treeify`` function, which users
can call to set a ``Model`` class up for MPTT. This function takes as
its arguments the ``Model`` class itself and, optionally, attribute
names for the set of fields which are required in order to manage the
database table for the ``Model`` as a tree.

The following argument is required:

* ``cls`` -- the ``Model`` class which is to be set up for MPTT.

All remaining arguments are optional, but you should take care to
specify appropriate attribute names where the default attribute names
do not fit with your ``Model`` class' definition:

* ``parent_attr`` -- the name of an attribute which relates the
  ``Model`` back to itself such that each instance can be a child of
  another instance. Defaults to ``'parent'``.

  Users are responsible for setting this field up on the model class,
  which can be done like so::

     parent = models.ForeignKey('self', null=True, blank=True, related_name='children')

For the following four arguments, if fields with the given attribute
names do not exist, they will be added to the ``Model`` class
dynamically:

* ``left_attr`` -- the name of an attribute which contains the left tree
  node edge indicator, which should be a ``PositiveIntegerField``.
  Defaults to ``'lft'``.

* ``right_attr`` -- the name of an attribute which contains the right
  tree node edge indicator, which should be a ``PositiveIntegerField``.
  Defaults to ``'rght'``.

* ``tree_id_attr`` -- the name of an attribute which contains the tree
  id of each node, which should be a ``PositiveIntegerField``. Defaults
  to ``'tree_id'``.

  Items which do not have a parent are considered to be "root" nodes in
  the tree and will be allocated a new tree id. All descendants of root
  nodes will be given the same tree id as their root node.

* ``level_attr`` -- the name of an attribute which contains the
  (zero-based) level at which an item sits in the tree, which should be
  a ``PositiveIntegerField``. Defaults to ``'level'``.

  For example, root nodes would have a level of ``0`` and their
  immediate children would have have a level of ``1``.

* ``tree_manager_attr`` -- the name of an attribute which should
  reference a custom ``Manager`` which is used to work with trees of
  ``Model`` instances. Defaults to ``'tree'``.

For more details on how these arguments are used to set up a ``Model``
class, see `MPTT setup process`_.

.. _`minimal example usage`:

A mimimal example usage of ``treeify`` is given below, where the
``Model`` class being set up for MPTT is suitable for use with the
default arguments of ``treeify``::

   from django.db import models

   from mptt.models import treeify

   class Genre(models.Model):
       name = models.CharField(max_length=50, unique=True)
       parent = models.ForeignKey('self', null=True, blank=True, related_name='children')

   treeify(Genre)


``Model`` instance methods
==========================

The following instance methods will be available on all instances of
``Model`` classes set up for MPTT:

* ``get_ancestors(ascending=False)`` -- creates a ``QuerySet``
  containing all the ancestors of the model instance.

  These default to being in descending order (root ancestor first,
  immediate parent last); passing ``True`` for the ``ascending``
  argument will reverse the ordering (immediate parent first, root
  ancestor last).

* ``get_descendants(include_self=False)`` -- creates a ``QuerySet``
  containing descendants of the model instance.

  If ``include_self`` is ``True``, the ``QuerySet`` will also include
  the model instance itself.

* ``get_descendant_count()`` -- returns the number of descendants the
  model instance has, based on its left and right tree node edge
  indicators. As such, this does not incur any database access.

* ``get_siblings(include_self=False)`` -- creates a ``QuerySet``
  containing siblings of the model instance. Root nodes are considered
  to be siblings of other root nodes.

  If ``include_self`` is ``True``, the ``QuerySet`` will also include
  the model instance itself.

* ``move_to(target, position)`` -- moves the model instance elsewhere in
  the tree based on ``target`` and ``position`` (when appropriate).

  .. note::
     Is is assumed that when you call this method, the tree fields in
     the instance you've called it on, and in any ``target`` instance passed
     in, reflect the current state of the database.

     Modifying the tree fields manually before calling this method or
     using tree fields which are out of sync with the database can
     result in the tree structure being put into an inaccurate state.

  If ``target`` is another model instance, it will be used to determine
  the type of movement which needs to take place, and will be used as
  the basis for positioning the model when it is moved, in combination
  with the ``position`` argument.

  A ``target`` of ``None`` indicates that the model instance should be
  turned into a root node. The ``position`` argument is disregarded in
  this case.

  Valid values for the ``position`` argument and their effects on
  movement are:

     ``'first-child'``
        The instance being moved should have ``target`` set as its new
        parent and be placed as its *first* child in the tree structure.

     ``'last-child'``
        The instance being moved should have ``target`` set as its new
        parent and be placed as its *last* child in the tree structure.

     ``'left'``
        The instance being moved should have ``target``'s parent set as
        its new parent and should be placed *directly before* ``target``
        in the tree structure.

     ``'right'``
        The instance being moved should have ``target``'s parent set as
        its new parent and should be placed *directly after* ``target``
        in the tree structure.

  A ``ValueError`` will be raised if an invalid value is given for the
  ``position`` argument.

  Note that some of the moves you could attempt to make with this are
  invalid - for example, trying to make an instance be its own its own
  child or the child of one of its descendants. In these cases, a
  ``mptt.exceptions.InvalidMove`` exception will be raised.

  The instance itself will be also modified as a result of this call, to
  reflect the state of its updated tree fields in the database, so it's
  safe to go on to save it or use its tree fields after you've called
  this method.


The ``TreeManager`` custom ``Manager``
======================================

This manager redefines the ``get_query_set`` method, such that the
contents of any ``QuerySet`` created using it will be ordered based on
the tree structure, with trees appearing in the order they were created
and their items being ordered in a depth-first fashion within each tree.

The following tree management methods are available:

* ``close_gap(size, target, tree_id)`` -- closes a gap of a certain size
  after the given target point (a left or right value) in the tree with
  the given id.

* ``create_space(size, target, tree_id)`` -- creates a space of a
  certain size after the given target point (a left or right value) in
  the tree with the given id.

* ``get_next_tree_id()`` -- determines the next available tree id for
  the ``Model`` being managed.

* ``make_child_node(root, target, position='last-child)`` -- moves
  ``node`` (which must be a root node) to a different tree, inserting it
  relative to the given ``target`` node in the new tree as specified by
  ``position``.

* ``make_root_node(node)`` -- removes ``node`` from its tree, making it
  the root node of a new tree.

* ``move_node(node, target, position='last-child')`` -- moves ``node``
  based on ``target``, relative to ``position`` when appropriate, taking
  care of calling the correct manager method to do so.

  A ``target`` of ``None`` indicates that ``node`` should be
  turned into a root node.

  The given ``node`` will be modified to reflect its new tree state in
  the database.

* ``move_to_new_tree(node, target, position='last-child')`` -- moves
  ``node`` to a different tree, inserting it relative to the given
  ``target`` node in the new tree as specified by ``position``.

* ``move_within_tree(node, target, position='last-child')`` --
  moves ``node`` within its current tree, relative to the given
  ``target`` node as specified by ``position``.


Template tags and filters
=========================

The ``mptt.templatetags.mptt_tags`` module defines template tags and
filters for working with trees of ``Model`` instances.

To use these, you must add ``mptt`` to your project's ``INSTALLED_APPS``
setting, then you can load the tag module in your templates using
``{% load mptt_tags %}``

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

full_tree_for_model
~~~~~~~~~~~~~~~~~~~

Populates a template variable with a ``QuerySet`` containing the full
tree for a given model.

Usage::

    {% full_tree_for_model [model] as [varname] %}

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

Example::

    {% full_tree_for_model tests.Genre as genres %}

Filter reference
----------------

tree_info
~~~~~~~~~

Given a list of tree items, produces doubles of a tree item and a
``dict`` containing information about the tree structure around the
item, with the following contents:

   new_level
      ``True`` if the current item is the start of a new level in the
      tree, ``False`` otherwise.

   closed_levels
      A list of levels which end after the current item. This will be an
      empty list if the next item is at the same level as the current
      item.

Using this filter with unpacking in a ``{% for %}`` tag, you should have
enough information about the tree structure to create a hierarchical
representation of the tree.

Example::

   {% for genre,tree in genres|tree_info %}
   {% if tree.new_level %}<ul><li>{% else %}</li><li>{% endif %}
   {{ genre.name }}
   {% for level in tree.closed_levels %}</li></ul>{% endfor %}
   {% endfor %}


Technical details
=================

MPTT setup process
------------------

The following steps are taken when ``treeify`` is called to set up a
``Model`` class for MPTT:

1. If the ``Model`` class does not have fields defined with names
   matching any of the attribute names given for the ``left_attr``,
   ``right_attr``, ``tree_id_attr`` or ``level_attr`` arguments, the
   following field is dynamically added to the model for each missing
   field::

      models.PositiveIntegerField(db_index=True, editable=False)

   As such, if all of these fields are left to be dynamically added, the
   tree structure is effectively invisible to the
   ``django.contrib.admin`` application and any ``newforms`` forms
   generated using the ``ModelForm`` class, if these are being used with
   the ``Model`` in question.

2. Signal receiving functions are registered for the built-in
   ``pre_save`` and ``pre_delete`` signals which are fired when a
   ``Model`` instance is about to be saved or deleted, respectively.

   These functions take care of managing MPTT fields when a ``Model``
   instance is about to be created or deleted and automatically
   adjusting tree details when an instance is saved after having its
   parent changed.

3. Additional instance methods are added to the ``Model`` class to
   retrieve information related to ``Model`` instances based on the
   tree structure and to move the instance around the tree.

   See `model instance methods`_ for details about the new methods which
   are available.

4. A custom ``Manager`` is added to the ``Model`` class to handle
   working with the tree of model instances. The ``tree_manager_attr``
   argument defines the attribute this manager will be publicly
   accessible via.

   A ``_tree_manager`` attribute is also set up to access this manager,
   for internal use by Django MPTT.

   See `the TreeManager custom Manager`_ for details about the tree
   operations available via this custom ``Manager``.

Dynamic field creation vs. explicitly defining MPTT fields
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

As the `minimal example usage`_ above demonstrates, ``Model`` classes
which do not have field names which clash with the default arguments to
the ``treeify`` function need not contain any details about MPTT
fields at all in their class definition.

One of the goals of this application is that authors should not have to
worry about setting up or using MPTT fields directly in order to reap
the benefits of the functionality their existence enables.

While dynamically added MPTT fields can be used in inner ``Meta`` and
``Admin`` classes, it is recommended that models which will use these
fields elsewhere in the class definition should implement the fields
themselves for clarity - it's already bad enough that we're dynamically
adding fields without your model definitions using fields which don't
appear to exist.

Salt to taste, your mileage may vary, etc. etc.

Running the tests
-----------------

The ``mptt.tests`` package is set up as an application which holds a
test settings module and defines models for use in testing MPTT. You can
run the tests from the command-line using the ``django-admin.py``
script,specifying that the test settings module should be used:

   django-admin.py test --settings=mptt.tests.settings

Django MPTT has been tested with SQLite, MySQL 4.1 and PostgreSQL 8.1 on
Windows.
