Source code for superdjango.db.parent.models

# -*- coding: utf-8 -*-

# Imports

from django.db import models
from django.utils.translation import ugettext_lazy as _
from .utils import get_ancestors, get_descendants

# Exports

__all__ = (
    "ParentTreeModel",
)

# Models


[docs]class ParentTreeModel(models.Model): """A simple implementation of tree-like functionality using only a ``parent`` field that refers to ``self``. Note that some methods will be horribly inefficient, yet effective at finding all of the records associated with the current instance. If performance is a concern, then you may wish to review the following resources: - `MPTT`_ - `Treebeard`_ .. _MPTT: https://django-mptt.readthedocs.io/en/latest/index.html .. _Treebeard: http://django-treebeard.readthedocs.io/en/latest/index.html """ cached_level = models.PositiveSmallIntegerField( _("level"), blank=True, null=True ) parent = models.ForeignKey( "self", blank=True, null=True, on_delete=models.SET_NULL, related_name="children", verbose_name=_("parent") ) class Meta: abstract = True
[docs] def get_ancestors(self): """Get the parent, grandparent, etc. for the current record. :rtype: list .. note:: The list is populated from nearest ancestor to furthest ancestor. Use ``reversed()`` as needed to re-order the list so that the top-most ancestor is first. """ return get_ancestors(self)
[docs] def get_children(self): """Get the child records that are directly connected to the current record. :rtype: django.db.models.QuerySet """ # noinspection PyUnresolvedReferences return self.children.all()
[docs] def get_descendants(self): """Get all of the descendants (children, grandchildren, etc.) for this record. :rtype: list """ return get_descendants(self)
[docs] def get_level(self, cached=True): """Get the level of the record within the hierarchy. :param cached: Indicates whether cache should be used. :type cached: bool :rtype: int """ if cached and self.cached_level: return self.cached_level if self.has_parent(): # noinspection PyUnresolvedReferences return self.parent.get_level() + 1 return 1
[docs] def get_siblings(self): """Get the records that are directly connected to the record's parent. :rtype: django.db.models.QuerySet | None """ if not self.has_parent(): return None # noinspection PyUnresolvedReferences return self.parent.children.exclude(pk=self.pk)
[docs] def has_children(self): """Indicates whether the record has child records associated with it. :rtype: bool """ # noinspection PyUnresolvedReferences return self.children.exists()
[docs] def has_parent(self): """Indicates whether this record has a parent record. :rtype: bool """ # noinspection PyUnresolvedReferences return self.parent_id is not None
[docs] def has_siblings(self): """Indicates whether this record's parent has other child records. :rtype: bool """ if not self.has_parent(): return False # noinspection PyUnresolvedReferences return self.parent.children.exclude(pk=self.pk).count() > 1
@property def level(self): """An alias of ``get_level()``.""" return self.get_level()
[docs] def save(self, *args, **kwargs): """Automatically save ``cached_level``.""" self.cached_level = self.get_level(cached=False) super().save(*args, **kwargs)