Возврат точных совпадений в верхней части Django Queryset
У меня есть модель django, называемая "пользователь", которая хранит некоторую основную информацию о людях, а именно имя и фамилию. В настоящее время у меня есть простой поиск по моей модели Django, где, если пользователь вводит первый имя, Django queryset возвращает первые 10 матчей, заказанных последние имя.
например, в настоящее время, если вы ищете "Sam", вы можете получить следующие результаты:
- Сэм Эббот
- Сэмюэл Бейкер!--11-->
- Сэмми Роджерс
- Сэм Симмонс
код для этого прост:
User.objects.filter(Q(first__istartswith=token)).order_by('last')
однако, я хочу изменить это так, что любой точно сначала возвращаются совпадения имен, а затем остальные результаты. Поэтому, если кто-то вводит "Sam", результаты должны быть:
- Сэм Эббот
- Сэм Симмонс
- Сэмюэл Бейкер
- Сэмми Роджерс!--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
сортировка по фамилии.