-
Notifications
You must be signed in to change notification settings - Fork 437
Description
Django has by now made it easy to perform case-insensitive-ish ordering of a queryset by running queryset.order_by(Lower("title"))
, or by using Lower("title").desc()
for reverse ordering.
I'd love to do this with django-tables2, but seeing as you have to pass a string to the order_by
field, I can't see an easy way to do so. I think using database functions (particularly Lower()
) in sorting is common enough that it would be nice to support it out of the box, so that as a django-tables2 user, I could define a column as tables.Column(order_by=(Lower("name"), "pk")
.
I'm currently doing this by using my own Column
subclass in most places. This isn't too bad, but as the implementation took some attempts to get right, I thought I'd share it here and ask for the feature in core :)
class FunctionOrderMixin:
order_by_function_lookup = {}
def __init__(self, *args, order_by=None, **kwargs):
if order_by and not isinstance(order_by, str):
if isinstance(order_by, Transform):
order_by = (order_by, )
# Pass a plain string list to Column so that it doesn't complain
plain_order_by = []
for key in order_by:
if isinstance(key, Transform):
plain_field = key.source_expressions[0].name
self.order_function_lookup[plain_field] = key
plain_order_by.append(plain_field)
order_by = plain_order_by
super().__init__(*args, order_by=order_by, **kwargs)
def order(self, queryset, is_descending):
if not self.order_function_lookup:
return (queryset, False)
mapped_order_by = []
for field in self.order_by:
if func := self.order_by_function_lookup.get(field):
if is_descending:
func = func.desc()
mapped_order_by.append(func)
else:
mapped_order_by.append(field)
queryset = queryset.order_by(*mapped_order_by)
return (queryset, True)
class SortableColumn(FunctionOrderMixin, tables.Column):
pass