Как отсортировать набор queryset по порядку следования Q-объектов?
У меня есть простая форма поиска и представление:
# forms.py
class SearchForm(forms.Form):
q = forms.CharField(label='Search', max_length=1024, required=False)
# views.py
def search(request):
search_form = forms.SearchForm()
q = ''
if request.method == 'GET':
search_form = forms.SearchForm(request.GET)
if search_form.is_valid():
q = search_form.cleaned_data['q']
products = models.Product.objects.filter(publication_status='published')
if q.strip() != '': # If there's something to search for which not whitespaces
products = products.filter(
Q(title__contains=q) |
Q(description__contains=q) |
Q(main__name__contains=q) |
Q(sub__contains=q)
)
context = {
'products': products,
'search_form':search_form,
}
return render(request, 'search.html', context=context)
# search.html
{% if products %}
<h3>Products:</h3>
<ul>
{% for p in products %}
<li><ul>
<li><a href="{% url 'product' p.pk %}">{{ p }}</a></li>
<li>{{ p.description }}</li>
<li><a href="{% url 'main' p.main.pk %}">{{ p.main }}</a></li>
<li><a href="{% url 'sub' p.main.pk p.sub %}">{{ p.sub|verbose }}</a></li>
</ul></li><br>
{% endfor %}
</ul>
{% else %}
<h3>No products found matching your search!</h3>
{% endif %}
Если слово "поло" является названием товара 2, а также описанием товара 1, Проблема в том, что когда я пытаюсь найти это слово, продукт 1 отображается на первом месте в шаблоне (потому что это первое соответствие).
Я пытаюсь отсортировать набор запросов сначала по названию, затем по описанию, затем по подкатегории, так что продукт 2 отображается первым (поскольку набор запросов отсортирован по порядку объектов Q)
Вам следует использовать пользовательский order_by
Для получения дополнительной информации о создании пользовательского заказа, вы можете прочитать эту страницу а для моделей Django это page
from django.db.models import Case, When, Value
custom_order=Case(
When(title__contains=q, then=Value(1)),
When(description__contains=q, then=Value(2)),
When(main__name__contains=q, then=Value(3)),
When(sub__contains=q, then=Value(4)))
products.filter(
Q(title__contains=q) |
Q(description__contains=q) |
Q(main__name__contains=q) |
Q(sub__contains=q)
).order_by(custom_order)
@lucas-grugru уже дал вам отличный ответ:
# views.py
def search(request):
search_form = forms.SearchForm()
query= Q(publication_status='published')
search_keys = ('title__contains', 'description__contains', 'main__name__contains', 'sub__contains')
if request.method == 'GET':
search_form = forms.SearchForm(request.GET)
if search_form.is_valid():
query &= Q(_connector=Q.OR, dict.fromkeys(search_keys, search_form.cleaned_data['q']))
context = {
'products': models.Product.objects.filter(query).order_by(key.removesuffix('_contains') for key in search_keys),
'search_form':search_form,
}
return render(request, 'search.html', context=context)
не забывайте, вы можете очистить q
в форме:
class SearchForm(forms.Form):
q = forms.CharField(label='Search', max_length=1024, required=False)
def clean_q(self):
q = (self.cleaned_data.get('q') or '').strip()
if q:
return q
raise ValidationError("Nothing to search")