Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Imports
3from django.conf import settings
4from django.db import models
5from django.db.models import F
6from django.utils.translation import ugettext_lazy as _
8# Exports
10__all__ = (
11 "SortedModel",
12)
14# Constants
16AUTH_USER_MODEL = settings.AUTH_USER_MODEL
18# Models
21class SortedModel(models.Model):
22 """Allow a model to be easily sortable.
24 **Fields**
26 - ``sort_order``
28 **Implementation**
30 .. code-block:: python
32 from superdjango.db.sorted.models import SortedModel
34 class Task(SortedModel):
35 # ...
36 class Meta:
37 ordering = ["sort_order"]
39 """
41 sort_order = models.IntegerField(
42 _("sort order"),
43 default=1,
44 help_text=_("How the record should be sorted.")
45 )
47 class Meta:
48 abstract = True
50 # noinspection PyUnusedLocal
51 @staticmethod
52 def pre_save_sort_order(sender, instance, **kwargs):
53 """Insure that the ``sort_order`` has a value.
55 .. code-block:: python
57 from django.db.models.signals import pre_save
58 from .models import Task
60 pre_save.connect(
61 Task.pre_save_sort_order,
62 Task,
63 dispatch_uid="task_pre_save_sort_order"
64 )
66 """
67 if not instance.sort_order or instance.sort_order == 0:
68 try:
69 # Get the previous sort order.
70 last = sender.objects.values("sort_order").order_by('-%s' % "sort_order")[0]
71 instance.sort_order = last["sort_order"] + 1
72 except IndexError:
73 # Set the sort order to 1 if this is the first record.
74 instance.sort_order = 1
76 def update_sort_order(self, sort_order, commit=True):
77 """Move the record to a different position, shifting the other records around it.
79 :param sort_order: The new position.
80 :type sort_order: int
82 :param commit: Indicates whether to save the record after the move.
83 :type commit: bool
85 """
87 # Force the new position to be an integer. This ensures the method works when interacting with a web-based API.
88 new_position = int(sort_order)
90 # The default position is always 1.
91 original_position = self.sort_order
93 # There's nothing to do if the new position isn't actually different.
94 if new_position == original_position:
95 return
97 # Prepare to move other records out of the way.
98 # noinspection PyTypeChecker
99 if new_position < original_position:
100 shift_amount = 1
101 # noinspection PyTypeChecker
102 select_range = (new_position, original_position - 1)
103 else:
104 shift_amount = -1
105 # noinspection PyTypeChecker
106 select_range = (original_position + 1, new_position)
108 # Shorten the statements by getting the class here.
109 cls = self.__class__
111 # Update records.
112 criteria = {
113 'sort_order__range': select_range,
114 }
116 # noinspection PyUnresolvedReferences
117 qs = cls.objects.filter(**criteria)
119 for row in qs:
120 row.update(field=F("sort_order") + shift_amount)
122 # Save the new position.
123 self.sort_order = new_position
125 if commit:
126 # noinspection PyUnresolvedReferences
127 self.save()