关于python:返回Django Queryset顶部的完全匹配

Return exact matches at top of Django Queryset

我有一个叫"用户"的django模型,它存储了一些关于人的基本信息,即名字和姓氏。我目前在我的django模型中有一个简单的搜索,如果用户输入名字,django查询集将返回前10个匹配项,按姓氏排序。

例如,当前,如果搜索"sam",可能会得到以下结果:

  • 山姆·阿伯特
  • 塞缪尔·贝克
  • 萨米·罗杰斯
  • 山姆·西蒙斯
  • 代码很简单:

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

    但是,我想修改这个方法,以便首先返回所有精确的名字匹配,然后返回其余的结果。因此,如果有人输入"sam",结果应该是:

  • 山姆·阿伯特
  • 山姆·西蒙斯
  • 塞缪尔·贝克
  • 萨米·罗杰斯
  • (精确的名字首先匹配,按姓氏排序,然后是按姓氏排序的其余匹配项)。

    我想把它转换成2个查询集,然后组合列表,但我想知道是否可以在1个查询中完成这一点,理想情况下,坚持使用基本的django查询集API(而不是编写一次性查询)。有人知道怎么做吗?

    事先谢谢。


    我个人并不推荐使用像Django Haystack这样的搜索引擎,但是如果你知道你使用的是什么数据库,你可以使用queryset.extra添加一个字段来排序记录:

    1
    2
    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')

    1
    2
    3
    4
    5
    6
    7
    # 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)

    另外,看看这个比您可能需要的更深入的问题:如何在django视图中组合2个或更多的查询集?


    我认为只使用一个查询(至少使用django-orm)是不可能做到这一点的。

    因此,您的两个查询应该如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    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个结果:

    1
    2
    3
    4
    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


    我认为你可能真的需要全文搜索来完成这一点。看看Djang Sphinx。

    如果您的示例与完整用例一样复杂,那么您可以在用户代码中处理排序和排序。


    可以按多个属性排序。

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

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