Created
July 12, 2019 21:10
-
-
Save frankwiles/74233a1261d41a21b8987bdd64773f82 to your computer and use it in GitHub Desktop.
Keeping Django Models Ordered Example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]: |
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
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.