"""
Sortation utilities are separated from the ``SortedModel`` in order to keep the model clean and prevent possible
collisions or conflicts with method names.
They may still be used in a declarative manner, for example:
.. code-block:: python
from superdjango.db.sorted import utils as sortation
from .models import Task
task = Task.objects.get(pk=1234)
if sortation.has_previous_record(task):
previous_task = sortation.get_previous_record(task)
if sortation.has_next_record(task):
next_task = sortation.get_next_record(task)
"""
# Exports
__all__ = (
"get_next_record",
"get_previous_record",
"has_next_record",
"has_previous_record",
"is_sortable",
)
# Functions
[docs]def get_previous_record(instance, **criteria):
"""Get the previous record, if one exists.
:param instance: The instance to be checked.
:param criteria: Additional criteria to be given. This is useful when
the record is embedded in a hierarchy.
:type criteria: dict
:rtype: models.Model
:returns: Returns the previous instance or ``None`` if a previous instance
could not be found.
"""
if not is_sortable(instance):
return None
cls = instance.__class__
fn = "sort_order"
try:
criteria['%s__lt' % fn] = instance.sort_order
return cls.objects.filter(**criteria).order_by("-%s" % fn)[0]
except IndexError:
return None
[docs]def get_next_record(instance, **criteria):
"""Get the next record, if one exists.
:param instance: The instance to be checked.
:param criteria: Additional criteria to be given. This is useful when
the record is embedded in a hierarchy.
:type criteria: dict
:rtype: models.Model
:returns: Returns the next instance or ``None`` if a next instance
could not be found.
"""
if not is_sortable(instance):
return None
cls = instance.__class__
fn = "sort_order"
try:
criteria['%s__gt' % fn] = instance.sort_order
return cls.objects.filter(**criteria).order_by("-%s" % fn)[0]
except IndexError:
return None
[docs]def has_next_record(instance, **criteria):
"""Indicates whether a record exists with a higher sort order.
:param instance: The instance to be checked.
:param criteria: Additional criteria to be given. This is useful when
the record is embedded in a hierarchy.
:type criteria: dict
:rtype: bool
"""
cls = instance.__class__
fn = "sort_order"
criteria['%s__gt' % fn] = instance.sort_order
return cls.objects.filter(**criteria).count() > 0
[docs]def has_previous_record(instance, **criteria):
"""Indicates whether a record exists with a lower sort order.
:param instance: The instance to be checked.
:param criteria: Additional criteria to be given. This is useful when
the record is embedded in a hierarchy.
:type criteria: dict
:rtype: bool
"""
cls = instance.__class__
fn = "sort_order"
criteria['%s__lt' % fn] = instance.sort_order
return cls.objects.filter(**criteria).count() > 0
[docs]def is_sortable(instance):
"""Determine whether a given instance is sortable.
:param instance: The instance to be checked.
:type instance: object
:rtype: bool
:returns: Returns ``True`` if the instance inherits from :py:class:`SortedModel` *or* a ``sort_order`` attribute is
defined on the instance.
"""
# Avoid import errors.
from .models import SortedModel
if isinstance(instance, SortedModel):
return True
elif hasattr(instance, "sort_order"):
return True
else:
return False