在Django Queryset顶部返回精确匹配

xwbd5t1u  于 2023-04-13  发布在  Go
关注(0)|答案(6)|浏览(88)

我有一个django模型,叫做“User”,它存储了一些关于人的基本信息,即名字和姓氏。我目前在我的django模型中进行了一个简单的搜索,如果用户输入firstname,django queryset将返回前10个匹配项,按lastname排序。
例如,目前,如果您搜索“Sam”,您可能会得到以下结果:
1.萨姆·阿伯特
1.塞缪尔·贝克
1.萨米·罗杰斯
1.山姆·西蒙斯
代码很简单:

User.objects.filter(Q(first__istartswith=token)).order_by('last')

然而,我想改变这一点,以便首先返回任何精确的名字匹配,然后是其余的结果。因此,如果有人输入“Sam”,结果应该是:
1.萨姆·阿伯特
1.山姆·西蒙斯
1.塞缪尔·贝克
1.萨米·罗杰斯
(精确的名字首先匹配,按姓氏排序,然后是按姓氏排序的其余匹配)。
我想把它变成2个查询集,然后只是组合列表,但我想知道是否有可能在1个查询中做到这一点,理想情况下坚持使用基本的django查询集API(而不是编写一次性查询)。有人知道如何做到这一点吗?
先谢谢你了。

bmp9r5qi

bmp9r5qi1#

我不认为只有一个查询就能做到这一点(至少在Django ORM中是这样)。
所以你的两个查询应该看起来像这样:

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
7gcisfzg

7gcisfzg2#

# 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)

也看看这个问题,它比你可能需要的更深入:How to combine 2 or more querysets in a Django view?

5lwkijsr

5lwkijsr3#

它并不漂亮,我个人建议使用搜索引擎,如django-haystack,但是,如果你知道你正在使用什么数据库,你可以使用QuerySet.extra添加一个字段来排序记录:

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')
qzwqbdag

qzwqbdag4#

我想你可能实际上需要全文搜索来实现这一点。看看djang-sphinx
如果你的例子和你的完整用例一样复杂,你可以在你的用户代码中处理排序和排序。

ddarikpa

ddarikpa5#

您可以按多个属性排序。

User.objects.filter(Q(first__istartswith=token)).order_by('first', 'last')

所以你首先按first排序,这样你的对象就可以根据精确匹配和后续匹配得到过滤器。然后你再按last排序,根据姓氏排序。

vtwuwzda

vtwuwzda6#

我认为这是最简单的解决方案,因为它只在一个SQL查询中计算:

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

User.objects.annotate(
    match_order=Case(
        When(first__iexact=token, then=Value(0)),
        default=Value(1)
    )
).order_by('match_order', 'last')

这将生成一个新的字段match_order,如果匹配是精确的,它的值最小。顺序是按该值,然后按姓氏。

相关问题