Возврат точных совпадений в верхней части Django Queryset

У меня есть модель django, называемая "пользователь", которая хранит некоторую основную информацию о людях, а именно имя и фамилию. В настоящее время у меня есть простой поиск по моей модели Django, где, если пользователь вводит первый имя, Django queryset возвращает первые 10 матчей, заказанных последние имя.

например, в настоящее время, если вы ищете "Sam", вы можете получить следующие результаты:

  1. Сэм Эббот
  2. Сэмюэл Бейкер!--11-->
  3. Сэмми Роджерс
  4. Сэм Симмонс

код для этого прост:

User.objects.filter(Q(first__istartswith=token)).order_by('last')

однако, я хочу изменить это так, что любой точно сначала возвращаются совпадения имен, а затем остальные результаты. Поэтому, если кто-то вводит "Sam", результаты должны быть:

  1. Сэм Эббот
  2. Сэм Симмонс
  3. Сэмюэл Бейкер
  4. Сэмми Роджерс!--11-->

(точное имя соответствует первому, отсортирован по фамилии, а затем остальные матчи отсортированы по фамилии).

Я думал о том, чтобы превратить это в 2 запроса, а затем просто объединить списки, но мне было интересно, можно ли это сделать в 1 запросе, в идеале придерживаясь базового API Django queryset (а не писать одноразовый запрос). Кто-нибудь знает, как это сделать?

спасибо заранее.

5 ответов


Я не думаю, что это действительно возможно сделать только с одним запросом (по крайней мере, с Django ORM).

поэтому ваши 2 запроса должны выглядеть так:

limit = 10
q1 = User.objects.filter(first__iexact=token).order_by('last')[:limit]
limit -= len(q1)
if limit:
    q2 = User.objects.exclude(pk__in=q1).filter(first__istartswith=token).order_by('last')[:limit]
else:
    q2 = []
users = list(q1) + list(q2)

другой способ сделать это-отфильтровать запрос в python, но вам нужно будет получить все результаты, а не только последние 10:

query = User.objects.filter(first__istartswith=token).order_by('last')
exacts = [user for user in query if user.first == token]
others = [user for user in query if user.first != token]
users = exacts + others

# Get exact matches first
qs1 = User.objects.filter(first__iexact=token).order_by('last')

# get secondary results second
qs2 = User.objects.filter(first__istartswith=token).exclude(qs1).order_by('last')

result = itertools.chain(qs1, qs2)

также взгляните на этот вопрос, который идет не глубже, чем вам, вероятно, нужно:Как объединить 2 или более запросов в представлении Django?


Это не очень и лично я бы рекомендовал использовать поисковую систему, такие как Джанго-сена но, если вы знаете, какую базу данных вы используете, вы можете использовать QuerySet.экстра добавить поле для заказа записи:

extra = {'is_exact': "%s.first LIKE '%s'" % (User._meta.db_table, token)}
User.objects.filter(Q(first__istartswith=token)).extra(select=extra).order_by('-is_exact', 'last')

Я думаю, что вам может понадобиться полнотекстовый поиск для этого. Проверьте Джанг-сфинкс.

Если ваш пример так же сложен, как и ваш полный вариант использования, вы можете просто обрабатывать сортировку и заказ в своем пользовательском коде.


вы можете заказать по нескольким атрибутам.

User.objects.filter(Q(first__istartswith=token)).order_by('first', 'last')

Так как с first Так что ваши объекты сделать фильтры по точному совпадению и последующие матчи. И тогда вы заказываете снова на last сортировка по фамилии.