Упорядочивание Django QuerySet по месту появления термина в результатах
Я пытаюсь реализовать автозаполнение для поля ввода с помощью Django и jQuery (пользователь ищет еду в меню). Проблема заключается в том, чтобы понять, как упорядочить результаты.
Если, например, пользователь набирает "Рыба", я хочу, чтобы автозаполнение было упорядочено следующим образом:
- "Рыба и чипсы"
- "Жареная рыба"
- "Жареная рыба"
- "Жареный сом"
Таким образом, результаты будут упорядочены в первую очередь по тому, где поисковый термин встречается в названии, а затем в алфавитном порядке.
Мой набор запросов в настоящее время извлекается в Django с помощью
views.py
def load_menu(request):
term = request.GET['term'] # the search term typed by user
menu = Menu.objects.filter(item__icontains=term).order_by('item', 'id') # item = name of food
# constructing JSON response
data = []
for item in menu:
item = {
'label': item.item,
'value': item.id,
}
data.append(item)
return JsonResponse({'data': data}) # received by jQuery function to display autocomplete menu
но это упорядочивает результаты только по алфавиту, чего я не хочу.
Как я могу сделать заказ, как указано в примере выше?
Вы достигаете этого, добавляя вспомогательное поле в набор запросов. Таким образом, вы можете включить свою специфическую логику в это поле и использовать ее в предложении order by
. Возьмем следующий пример: order_by_position
добавляется с помощью annotate
и case
when
и присваивается 1, 2, 3 на основе следующих условий:
1
если элемент начинается с термина,
2
если элемент содержит термин, и
3
, если элемент не содержит термин.
Наконец, запрос упорядочивается по 'order_by_position', 'item'
, что гарантирует, что он сначала упорядочен по определенному порядку в order_by_position
, а затем в алфавитном порядке на item
.
from django.db.models import Case, Value, When, CharField
def load_menu(request):
term = request.GET['term'] # the search term typed by user
menu = Menu.objects.annotate(
order_by_position=Case(
When(item__startswith=term, then=Value(1)),
When(item__contains=term, then=Value(2)),
default=Value(3),
output_field=CharField(),
)
).filter(item__icontains=term).order_by('order_by_position', 'item')
# constructing JSON response
data = []
for item in menu:
item_data = {
'label': item.item,
'value': item.id,
}
data.append(item_data)
return JsonResponse({'data': data})
Чтобы пойти еще дальше и быть более конкретным, вы можете также рассмотреть индекс совпадающего термина:
from django.db.models import CharField, Value, Func, PositionField
class StartsWith(Func):
function = 'POSITION'
arity = 2
output_field = PositionField()
def load_menu(request):
term = request.GET['term'] # the search term typed by user
menu = Menu.objects.annotate(
start_pos=StartsWith(F('item'), Value(term)),
).filter(item__icontains=term).order_by('start_pos', 'item')
# constructing JSON response
data = []
for item in menu:
item_data = {
'label': item.item,
'value': item.id,
}
data.append(item_data)
return JsonResponse({'data': data})