django - 如何在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中,我设法查询了数据库并根据所有匹配的单词显示了所有结果,但这里缺少的一点是我只设法按书签数量对结果进行排序。对于即:我有这两个对象:
Object 1 : name = How to use django forms ?
Object 2 : name = Using django forms for searching the database.
对象 1 被 20 个用户添加书签,对象 2 被 10 个用户添加书签,我在搜索栏中输入:使用 django 表单数据库 结果我有第一个对象作为列表中显示的第一个答案,即使第二个有与搜索的关键字匹配更多。所以我在这里要做的是首先根据匹配关键字的数量对结果进行排序,然后按书签数量对其进行排序。到目前为止,这是我的观点:
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')
全部一起
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')
推荐阅读
- css - Mozilla firefox 中未按预期显示来自以下 css 的设计
- angular - 如何在 JSON 数据上使用管道更改货币 - ionic 3
- koa - 清理 Koa 上的用户输入?
- javascript - 使用 Javascript 从 Json 中的子元素创建新键
- c# - codedui 中的 MSAA 以下技术是否不支持过滤器属性?
- python - 在列表python中拆分元素
- python - 我可以将我的 selenium 脚本放入 python gui 应用程序吗?
- ignite - 为什么我的 ignite 应用程序会抛出异常,例如“java.lang.IllegalStateException:缓存已关闭或销毁:”
- azure - 是否可以在 Azure DevOps 的工作项类型中创建自定义工作项类型?
- python - Python Selenium Recaphta 我不是机器人 - 如何绕过