Photo by Edu Grande on Unsplash
Let's say you have a Choice field on your model. And you want to order by that choice labels. It's a no-brainer if values and labels are the same. Because values are stored in the database and it's the one in charge of ordering.
from django.db.models import TextChoices
class State(TextChoices):
STARTED = 'STARTED', 'Started'
WORKING = 'WORKING', 'Working'
FINISHED = 'FINISHED', 'Finished'
This will work out of the box:
from django_filters import (
FilterSet,
...,
OrderingFilter,
)
class MyFilter(FilterSet):
...some fields filtering declaration...
ordering = OrderingFilter(
fields: {
'state': 'state',
}
)
class Meta:
model = MyModel
But if labels differ from values it just won't work. Because now the database doesn't know anything about labels.
from django.db.models import TextChoices
class State(TextChoices):
STARTED = 'STARTED', 'Initiated'
WORKING = 'WORKING', 'Operating'
FINISHED = 'FINISHED', 'Stopped'
What you'd need to do now is to sort State labels and then create Case > When > Then
chains. This way the database will know how you want to order results (but it's still oblivious about the whole "labels" thing).
class MyOrderingFilter(OrderingFilter):
def filter(self, qs, value):
state = None
if value:
for idx, param in enumerate(value):
if param.strip('-') == 'state':
state = value.pop(idx)
qs = super(MyOrderingFilter, self).filter(qs, value)
if state:
sorted_states = {
val[0]: idx
for idx, val in enumerate(
sorted(
State.choices,
key=lambda x: x[1],
reverse='-' in state,
)
)
}
qs = qs.order_by(
Case(
*[
When(state=key, then=Value(val))
for key, val in sorted_states.items()
]
)
)
return qs
And then use MyOrderingFilter
instead of OrderingFilter
in MyFilter
from the snippet above. The magic happens inside sorted_states = {...}
dict comprehension. We tell our DB that we want our values with label "Initiated" to be first (or rather zero) in order, values with "Operating" to be second and so on. And when we're building qs = qs.order_by(Case(...))
we're just constructing the query itself.
So now your ordering will work according to the choice labels and not it's values.
Top comments (0)