# Imports
from django.conf import settings
from django.db import models
from django.db.models import F
from django.utils.translation import ugettext_lazy as _
# Exports
__all__ = (
"SortedModel",
)
# Constants
AUTH_USER_MODEL = settings.AUTH_USER_MODEL
# Models
[docs]class SortedModel(models.Model):
"""Allow a model to be easily sortable.
**Fields**
- ``sort_order``
**Implementation**
.. code-block:: python
from superdjango.db.sorted.models import SortedModel
class Task(SortedModel):
# ...
class Meta:
ordering = ["sort_order"]
"""
sort_order = models.IntegerField(
_("sort order"),
default=1,
help_text=_("How the record should be sorted.")
)
class Meta:
abstract = True
# noinspection PyUnusedLocal
[docs] @staticmethod
def pre_save_sort_order(sender, instance, **kwargs):
"""Insure that the ``sort_order`` has a value.
.. code-block:: python
from django.db.models.signals import pre_save
from .models import Task
pre_save.connect(
Task.pre_save_sort_order,
Task,
dispatch_uid="task_pre_save_sort_order"
)
"""
if not instance.sort_order or instance.sort_order == 0:
try:
# Get the previous sort order.
last = sender.objects.values("sort_order").order_by('-%s' % "sort_order")[0]
instance.sort_order = last["sort_order"] + 1
except IndexError:
# Set the sort order to 1 if this is the first record.
instance.sort_order = 1
[docs] def update_sort_order(self, sort_order, commit=True):
"""Move the record to a different position, shifting the other records around it.
:param sort_order: The new position.
:type sort_order: int
:param commit: Indicates whether to save the record after the move.
:type commit: bool
"""
# Force the new position to be an integer. This ensures the method works when interacting with a web-based API.
new_position = int(sort_order)
# The default position is always 1.
original_position = self.sort_order
# There's nothing to do if the new position isn't actually different.
if new_position == original_position:
return
# Prepare to move other records out of the way.
# noinspection PyTypeChecker
if new_position < original_position:
shift_amount = 1
# noinspection PyTypeChecker
select_range = (new_position, original_position - 1)
else:
shift_amount = -1
# noinspection PyTypeChecker
select_range = (original_position + 1, new_position)
# Shorten the statements by getting the class here.
cls = self.__class__
# Update records.
criteria = {
'sort_order__range': select_range,
}
# noinspection PyUnresolvedReferences
qs = cls.objects.filter(**criteria)
for row in qs:
row.update(field=F("sort_order") + shift_amount)
# Save the new position.
self.sort_order = new_position
if commit:
# noinspection PyUnresolvedReferences
self.save()