如何在 Django 中使用关键字搜索后显示 objects 的结果进行排序

How to sort a result of objects shown after a search with keyword in Django

实际上我有一个工作代码,但我面临的问题是如何根据多个规则对查询集的结果进行排序。 这是我的 models.py :

class Node(MPTTModel):
    parent              = TreeForeignKey('self', on_delete=models.CASCADE, blank=True, null=True, related_name='children')
    name                = models.TextField(blank=True, null=True)`
    viewed_by           = models.ManyToManyField(CustomUser, related_name='viewed_by',  blank=True)
    bookmarked_by       = models.ManyToManyField(CustomUser, related_name='bookmarked_by', blank=True)
    thumbs_up           = models.ManyToManyField(CustomUser, related_name='thumbs_up', blank=True)

在我的 views.py 中,我设法查询了数据库并显示了基于所有匹配词的所有结果,但这里缺少的一点是我只设法按书签数量对结果进行排序. 对于即: 我有这两个 objects :

Object 1 : name = How to use django forms ?
Object 2 : name = Using django forms for searching the database.

object 1 被 20 个用户收藏,Object 2 被 10 个用户收藏,我在搜索栏中输入:Using django forms database 在结果中,我将第一个 object 作为列表中显示的第一个答案,即使第二个答案与搜索到的关键字有更多匹配项。 所以我想在这里做的是首先根据匹配关键字的数量对结果进行排序,然后根据书签的数量对其进行排序。 这是我目前的看法:

search_text_imported = request.session['import_search_text']
if search_text_imported != '':
    result_list = []
    get_result_list = [x for x in search_text_imported.split() if len(x) > 2]
    for keyword in get_result_list:
        tree_list = Node.objects.filter((Q(name__icontains=keyword) | Q(Tags__icontains=keyword)), tree_type='root', published=True ).annotate(num_bookmarks=Count('bookmarked_by')).order_by('-num_bookmarks')
        result_list += list(tree_list)
        result = list(OrderedDict.fromkeys(result_list))
    context = {
    'tree_result': result,
    }

如果这里遗漏了什么,请告诉我,我们将不胜感激。

您遇到的问题是由于您是通过将查询结果连接在一起来创建结果列表的,如果您连接它们,查询是否排序并不重要。您可以通过首先创建 Q 过滤器然后将其传递给单个查询来更改代码以仅执行单个排序查询

filter = Q()
for keyword in keywords:
    filter |= Q(name__icontains=keyword)
    filter |= Q(Tags__icontains=keyword
result = Node.objects.filter(
    filter,
    tree_type='root',
    published=True
).annotate(
    num_bookmarks=Count('bookmarked_by')
).order_by('-num_bookmarks')

按匹配到的关键词数量排序是个难题。一个潜在的解决方案是根据是否匹配,为每个关键字用 1 或 0 注释每个节点,然后将它们全部相加

from functools import reduce
from operator import add

from django.db.models import Case, When, Value, F

cases = {}
for i, keyword in enumerate(keywords):
    cases[f'keyword_match_{i}'] = Case(
        When(name__icontains=keyword, then=1),
        default=Value(0),
        output_field=models.IntegerField(),
    )

Node.objects.annotate(**cases).annotate(
    matches=reduce(add, (F(name) for name in cases))
).order_by('-matches')

一起[=13​​=]

filter = Q()
cases = {}
for i, keyword in enumerate(keywords):
    filter |= Q(name__icontains=keyword)
    filter |= Q(Tags__icontains=keyword
    # Case is basically an "if" statement
    # If the condition matches then we set the annotated value to 1
    cases[f'keyword_match_{i}'] = Case(
        When(name__icontains=keyword, then=1),
        default=Value(0),
        output_field=models.IntegerField(),
    )
result = Node.objects.filter(
    filter,
    tree_type='root',
    published=True
).annotate(
    **cases
).annotate(
    num_bookmarks=Count('bookmarked_by'),
    keywords_matched=reduce(add, (F(name) for name in cases))
).order_by('-keywords_matched', '-num_bookmarks')