Как получить список всех отличительных первых букв заданного поля

Допустим, у меня есть класс Person, который состоит из поля last_name string.

Я хочу отобразить ссылки на все первые буквы имен, существующих в базе данных.

Например: A B D E... когда есть Адамс, Браун, Дуглас, Эванс и нет никого, чья фамилия начинается с C.

Конечно, представление здесь не является проблемой, так как я хочу подготовить все это на бэкенде. Поэтому вопрос в том, как написать хорошую функцию модели или представления, которая обеспечит это.

Я бы хотел, чтобы он был независим от БД, однако, трюки для любой конкретной БД были бы бонусом.

Кроме того, я бы хотел, чтобы это было достаточно хорошо оптимизировано с точки зрения скорости, потому что это может быть много имен. Он должен быть менее наивным, чем этот самый простой алгоритм:

  1. Get all people
  2. Create set (because of the uniqueness of elements) of the first letters
  3. Sort and return

Так, например (в файле views.py):

    names = Person.objects.values_list('last_name', flat=True)
    letters = {name[0] for name in names}
    letters_sorted = sorted(letters)

Конечно, я могу order_by первым или присвоить новый атрибут каждому объекту (содержащий первую букву), но я не думаю, что это ускорит процесс.

Я также думаю, что предположение о том, что все буквы используются является плохим предположением, если я буду проверять каждую букву, существует ли хотя бы одно имя для этой буквы ;-)

Какой подход будет наиболее эффективным для баз данных и django здесь?

Я бы сначала отсортировал имена, затем распаковал имена как аргументы для zip, затем использовал бы только первый кортеж, который выдает zip:

names = sorted(Person.objects.values_list('last_name', flat=True))

first_letters = next(zip(*names))

Это не использует наборы, не удаляет дубликаты или что-то подобное. Это критично? Если да, то можно сделать так:

names = Person.objects.values_list('last_name', flat=True)

first_letters = sorted(set(next(zip(*names))))

Хотя это вряд ли будет более производительным, чем то, что вы уже написали.

import string
alphabet_list = list(string.ascii_lowercase) + list(string.ascii_uppercase)
result = dict(map(lambda x: (x, Person.objects.filter(name__start_with=x).exists()), alphabet_list))

Вы также можете использовать методы orm для генерации списка инициалов (включая count):

from django.db.models import Count
from django.db.models.functions import Left

initials = (Person
            .objects
            .annotate(initial=Left('last_name', 1))
            .values('initial')
            .annotate(count=Count('initial'))
            .order_by('initial'))

В результате получится что-то вроде

<QuerySet [{'initial': 'A', 'count': 1},
           {'initial': 'B', 'count': 2},
           ...
           {'initial': 'Y', 'count': 1}]>
Вернуться на верх