Фильтр списка администраторов, сгруппированный по первой букве и расширенной группе

У меня есть исходный код модели с внешним ключом для размещения модели. На странице списка изменений исходной модели мне бы хотелось, чтобы фильтр списка отображал все буквы алфавита, а при нажатии на "A" отображались все названия мест, начинающиеся с "A", которые можно щелкнуть, чтобы отобразить только те источники в списке, у которых есть это место в поле fk.

Упрощенные модели:

class Source(models.Model):
    place = models.ForeignKey(Place, null=True, place=True, on_delete=models.SET_NULL

class Place(models.Model):
    name = models.CharField(max_length = 255, primary_key=True)

На данный момент у меня есть вот это:

class PlaceFilter(admin.SimpleListFilter):
    title = 'first letter of place name'
    parameter_name = 'letter'

    def lookups(self, request, model_admin):
        qs = model_admin.get_queryset(request)
        options = qs.values_list(Substr('place__name', 1, 1), Substr('place__name', 1, 1)) \
                    .annotate(fl=Count(Substr('place__name', 1, 1))).distinct() \
                    .order_by(Substr('place__name', 1, 1))
        options = [(i, f'{j} ({k})') for i,j,k in options]

        return options

    def queryset(self, request, queryset):
        if self.value():
            return queryset.filter(place__name__istartswith=self.value())

В котором отображаются все буквы, но при нажатии на букву список содержит все источники с указанием места, начинающегося с этой буквы. Кроме того, я нахожусь в Нидерландах, и названия некоторых мест здесь начинаются на "с", "т", иногда с дефисом, иногда без него. Эти имена должны быть сгруппированы не по начальной одинарной кавычке, а (обычно) по 4-й букве, т.е. "s-Gravenhage" должно быть под "G".

Я не знаю, нужен ли мне здесь счетчик, я бы предпочел, чтобы он не всегда отображался в списке, но если я попытаюсь использовать встроенную функцию подсчета в фильтрах списка, я продолжу получать ошибки примерно такого типа:

ValueError at /admin/importer/source/
Column aliases cannot contain whitespace characters, quotation marks, semicolons, or SQL comments.

Я перепробовал несколько разных вариантов, даже отфильтровал имена, прежде чем перейти к методу de queryset().

ОБНОВЛЕНИЕ:
Теперь, используя приведенный ниже код, я получаю список букв алфавита, и при нажатии на одну из них появляются названия мест, начинающиеся на эту букву, и они корректно отображаются. Но я по-прежнему получаю сообщение об ошибке в отношении псевдонимов столбцов, когда нажимаю "показать количество"...

class PlaceFilter(admin.SimpleListFilter):
    title = 'first letter of place name'
    parameter_name = 'letter'

    def lookups(self, request, model_admin):
        qs = model_admin.get_queryset(request)
        letters = list(string.ascii_uppercase)
        options = [(letter, letter) for letter in letters]
        if val := self.value():
            print(val)
            if len(val) == 1:
                sub_options = list(qs.values_list('place__name', 'place__name').distinct() \
                            .filter(place__name__iregex=rf"^('s-|'s )?{val}") \
                            .order_by('place__name'))
                val_index = options.index((val, val))

                index = val_index + 1
                for option in sub_options:
                    options.insert(index, option)
                    index += 1
                        
        return list(options)
    
    def queryset(self, request, queryset):
        if self.value() and len(self.value()) > 1:
            return queryset.filter(place__name=self.value())

Я решил эту проблему с помощью этого кода. Теперь, даже когда я нажимаю на название места, другие названия с той же первой буквой остаются открытыми. И счетчик тоже работает :)

class PlaceFilter(admin.SimpleListFilter):
    title = 'first letter of place name'
    parameter_name = 'letter'

    def get_sub_options(self, letter, qs):
        return list(qs.values_list('place__name', 'place__name').distinct() \
                .filter(place__name__iregex=rf"^('s-|'s |'t )?{letter}") \
                .order_by('place__name'))

    def lookups(self, request, model_admin):
        qs = model_admin.get_queryset(request)
        letters = list(string.ascii_uppercase)
        options = [(letter, letter) for letter in letters]
        if self.value():
            if len(self.value()) == 1:
                first_letter = self.value()
                sub_options = self.get_sub_options(first_letter, qs)
                val_index = options.index((self.value(), self.value()))

                index = val_index + 1
                for option in sub_options:
                    options.insert(index, option)
                    index += 1
            elif len(self.value()) > 1:
                first_letter = re.sub(fr"^('s-|'s |'t )", "",self.value())[0]
                sub_options = self.get_sub_options(first_letter, qs)
                val_index = options.index((first_letter, first_letter))

                index = val_index + 1
                for option in sub_options:
                    options.insert(index, option)
                    index += 1
                        
        return list(options)
    
    def queryset(self, request, queryset):
        if self.value():
            if len(self.value()) == 1:
                return queryset.filter(place__name__istartswith=self.value())
            if len(self.value()) > 1:
                return queryset.filter(place__name=self.value())

Вернуться на верх