Let's say I have the following Django model:
class Todo(models.Model):
class Priority(models.IntegerChoices):
HIGH = 1, "High"
LOW = 2, "Low"
MEDIUM = 3, "Medium"
title = models.CharField(max_length=255)
priority = models.PositiveSmallIntegerField(choices=Priority.choices, db_index=True)
And the following test data via factory-boy:
import factory
from factory.django import DjangoModelFactory
class TodoFactory(DjangoModelFactory):
class Meta:
model = Todo
title = factory.Sequence(lambda n: f"Todo {n}")
def test_custom_db_sort():
todo1 = TodoFactory(priority=Todo.Priority.MEDIUM)
todo2 = TodoFactory(priority=Todo.Priority.HIGH)
todo3 = TodoFactory(priority=Todo.Priority.LOW)
todo4 = TodoFactory(priority=Todo.Priority.MEDIUM)
todo5 = TodoFactory(priority=Todo.Priority.HIGH)
I want to fetch the Todo
objects from the database and sort from HIGH
-> LOW
priority. Unfortunately Todo.Priority
isn't ordered correctly so I can't sort on that. I've come up with the following solution:
PREFERENCE = {
Priority.HIGH: 1,
Priority.MEDIUM: 2,
Priority.LOW: 3,
}
result = sorted(
Todo.objects.all(),
key=lambda x: [PREFERENCE[x.priority], x.id]
)
This works but I'd rather sort in the database for several reasons (e.g. faster, better to do work in the DB rather than Python). Is there anyway to perform this query in the database?
CodePudding user response:
Use Conditional expression (Case(...)
, When(...)
) expressions
from django.db.models import Case, When, Value
new_priority = Case(
When(priority=Todo.Priority.HIGH, then=Value(1)),
When(priority=Todo.Priority.MEDIUM, then=Value(2)),
When(priority=Todo.Priority.LOW, then=Value(3)),
)
qs = Todo.objects.annotate(new_priority=new_priority).order_by("new_priority")
If I have a choice, I would rather update the Priority
choice values instead of annotating the expression.