返回Django Queryset顶部的完全匹配

时间:2012-07-10 05:03:40

标签: python django django-queryset

我有一个名为“用户”的django模型,它存储了一些关于人的基本信息,即名字和姓氏。我目前在我的django模型中进行了简单的搜索,如果用户输入第一个名称,则django queryset将返回前10个匹配项,按 last 名称排序。< / p>

例如,目前,如果您搜索“Sam”,您可能会得到以下结果:

  1. Sam Abbott
  2. Samuel Baker
  3. Sammy Rogers
  4. Sam Simmons
  5. 这个代码很简单:

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

    但是,我想更改此内容,以便首先返回任何完全第一个名称匹配,然后返回其余结果。因此,如果有人输入“Sam”,结果应该是:

    1. Sam Abbott
    2. Sam Simmons
    3. Samuel Baker
    4. Sammy Rogers
    5. (确切的名字首先匹配,按姓氏排序,然后是按姓氏排序的其余匹配)。

      我想把它变成2个查询集,然后只是组合列表,但我想知道是否可以在1个查询中执行此操作,理想情况下坚持使用基本的django queryset API(而不是编写一次性查询)。有谁知道这样做的方法吗?

      提前致谢。

5 个答案:

答案 0 :(得分:3)

我认为只用一个查询(至少使用Django ORM)就可以做到这一点。

所以你的2个查询应该是这样的:

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

答案 1 :(得分:3)

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

答案 2 :(得分:3)

它不漂亮,我个人推荐使用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')

答案 3 :(得分:1)

我认为您可能实际上需要全文搜索来实现这一目标。查看djang-sphinx

如果您的示例与完整用例一样复杂,您只需处理用户代码中的排序和排序。

答案 4 :(得分:0)

您可以按多个属性订购。

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

因此,您首先按first订购,以便您的对象根据完全匹配和后续匹配获得过滤器。然后您再次在last上订购,以便根据姓氏进行排序。