Welcome to django-closuretree’s documentation!¶
django-closuretree is an implementation of a closure tree for tree-based Django models. It aims to reduce the number of database hits required when traversing complex tree-based relationships between models in your Django app.
Requirements¶
- Django 1.4+
- Sphinx (for documentation)
Basic Usage¶
Inherit your models from closuretree.models.ClosureModel
instead of django.db.models.Model
:
from django.db import models
from closuretree.models import ClosureModel
class MyModel(ClosureModel):
parent = models.ForeignKey('self', related_name='children')
name = models.CharField(max_length=32)
def __unicode__(self):
return '%s: %s" % (self.id, self.name)
django-closuretree will automatically use the field named parent
as the
relationship. This can be manually overriden:
from django.db import models
from closuretree.models import ClosureModel
class MyModel(ClosureModel):
parent_rel = models.ForeignKey('self', related_name='children')
name = models.CharField(max_length=32)
class ClosureMeta(object):
parent_attr = 'parent_rel'
def __unicode__(self):
return '%s: %s' % (self.id, self.name)
Perhaps the most useful methods provided by closuretree.models.ClosureModel
are
the following:
>> my_model = MyModel.objects.get(pk=10)
>> my_model.get_ancestors()
[<MyModel: 1: Foo>, <MyModel: 2: Bar>, <MyModel: 3: Fish>]
>> my_model.get_descendants()
[<MyModel: 11: Bob>, <MyModel: 12: Alice>]
>> my_model.get_descendants(depth=1)
[<MyModel: 11: Bob>]
>> my_model.get_root()
<MyModel: 1: Foo>
>> my_model.is_ancestor_of(MyModel.objects.get(name='Alice'))
True
>> my_model.is_descendant_of(MyModel.objects.get(name='Bar'))
True
Read the closuretree Package model documentation for more methods.
Adding to existing models¶
If you add django-closuretree to existing models, you’ll need to build the closure table for the pre-existing data:
MyModel.rebuildtable()
Indirect relations¶
If your model is linked to itself via an indirect relationship (for example, ModelA -> ModelB -> ModelC -> ModelA), then you’ll need to define a parent property that traverses this relationship, and set a sentinel attribute as the foriegn key to ModelB:
class ModelA(ClosureModel):
model_b = models.ForeignKey(ModelB)
@property
def parent(self):
return self.model_b.model_c.model_a
class ClosureMeta:
sentinel_attr = 'model_b'
Closuretree will watch the sentinel attribute for changes, and use the value of the parent property when rebuilding the tree.
Using the update
QuerySet method¶
If you change the parent field of a model (or number of models) using the QuerySet update
method (i.e. MyModel.objects.filter(...).update(parent=...)`
) you’ll need to rebuild the closure table for that model manually, as the pre- and post-save signal handlers are not called:
MyModel.rebuildtable()