Articles

Tidy Caseless Ordering in Django

Tidy Caseless Ordering in Django

Tidy Caseless Ordering

Sorting by name in Django is a common admin annoyance. By default upper-case names come before lower-case ones because of their raw ASCII encodings. This isn't fun to try and explain to clients.

For years I've ordered my models using a forced lower-case slug, but sometimes you need the big guns: database functions. Let's use that powerful ordering, while keeping our code nice and neat.

What's the Problem?

Let's start with the simplest of simple Django models. The admin changelist for these models uses strict alphanumeric ordering by default. Upper-case names like 'Zed' would come before the lower-case 'adam'.

from django.db import models

class Names(BaseModel):
    name = models.CharField(max_length=63)

    class Meta:
        ordering = ('name',)

Query Expressions to the Rescue

For years now (since Django v2.0) the ordering model meta option has supported query expressions in addition to the usual field names. Unfortunately, the syntax is a little too verbose for my tastes:

from django.db import models

class Names(BaseModel):
    name = models.CharField(max_length=63)

    class Meta:
        ordering = (models.Func(models.F('name'), function='LOWER'),)

Tidy Up and Finish

Much better to extract and abstract away the messiness into a simple utility function. This function is only called one, during Django start-up.

from django.db import models

def caseless(field_name: str) -> models.Func:
    return models.Func(models.F(field_name), function='LOWER')

class Names(BaseModel):
    name = models.CharField(max_length=63)

    class Meta:
        ordering = (caseless('name'),)

Updated 28 Jan 2024, first published 30 Oct 2023