Skip to content

Instantly share code, notes, and snippets.

@frankwiles
Created July 12, 2019 21:10
Show Gist options
  • Save frankwiles/74233a1261d41a21b8987bdd64773f82 to your computer and use it in GitHub Desktop.
Save frankwiles/74233a1261d41a21b8987bdd64773f82 to your computer and use it in GitHub Desktop.
Keeping Django Models Ordered Example
from django.db import models, transaction
from django.db.models import F, Max
class StepManager(models.Manager):
"""
Manager to encapsulate bits of business logic
"""
def move(self, obj, new_order):
""" Move an object to a new order position """
qs = self.get_queryset()
with transaction.atomic():
if obj.order > int(new_order):
qs.filter(
task=obj.task, order__lt=obj.order, order__gte=new_order
).exclude(pk=obj.pk).update(order=F("order") + 1)
else:
qs.filter(
task=obj.task, order__lte=new_order, order__gt=obj.order
).exclude(pk=obj.pk).update(order=F("order") - 1)
obj.order = new_order
obj.save()
def create(self, **kwargs):
instance = self.model(**kwargs)
with transaction.atomic():
# Get our current max order number
results = self.filter(task=instance.task).aggregate(Max("order"))
current_order = results["order__max"]
if current_order is None:
current_order = 0
value = current_order + 1
instance.order = value
instance.save()
return instance
class Task(models.Model):
name = models.CharField(max_length=100)
class Step(models.Model):
task = models.ForeignKey(Task, related_name="steps", on_delete=models.CASCADE)
name = models.CharField(max_length=100)
order = models.IntegerField(default=1)
objects = StepManager()
from tasks.models import *
t = Task.objects.create(name="Test1")
s1 = Step.objects.create(task=t, name="Testing1")
s2 = Step.objects.create(task=t, name="Testing2")
s3 = Step.objects.create(task=t, name="Testing3")
s4 = Step.objects.create(task=t, name="Testing4")
# Show the existing numbers/structure
In [9]: for s in Step.objects.all():
...: print(f"pk={s.pk} order={s.order} name={s.name}")
...:
pk=1 order=1 name=Testing1
pk=2 order=2 name=Testing2
pk=3 order=3 name=Testing3
pk=4 order=4 name=Testing4
# Move the 4th item to the 2nd position
In [10]: Step.objects.move(s4, 2)
In [11]: for s in Step.objects.all():
...: print(f"pk={s.pk} order={s.order} name={s.name}")
...:
pk=1 order=1 name=Testing1
pk=2 order=3 name=Testing2
pk=3 order=4 name=Testing3
pk=4 order=2 name=Testing4
In [12]:
@frankwiles
Copy link
Author

Oh I think I see your issue. These model objects, the ones NOT moving are not going to get their values updated in an ipython shell. You have to requery them to see the updated order values. If you use my code and re-query the Steps each time you'll see the movement.

@Jwrecker
Copy link

Thanks, I figured it out. Your code worked great, sorry for doubting you :). Thanks for taking time to help me out! I really appreciate it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment