2012-07-10 53 views
8

我有一个名为“User”的django模型,它存储一些关于人的基本信息,即名和姓。我目前在我的django模型中进行了简单的搜索,其中,如果用户键入名称的第一个名称,则django queryset返回前10个匹配项,由最后名称排序。在Django Queryset顶部返回完全匹配

例如,目前,如果你搜索 “萨姆”,你可能会得到如下结果:

  1. 山姆雅培
  2. 塞缪尔·贝克
  3. 萨米·罗杰斯
  4. 萨姆·西蒙斯

这个代码很简单:

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

但是,我想对此进行更改,以便任何确切名称匹配首先返回,其次是其余结果。因此,如果在“山姆”有人类型,其结果应改为:

  1. 山姆雅培
  2. 萨姆·西蒙斯
  3. 塞缪尔·贝克
  4. 萨米·罗杰斯

(精确第一的名头相匹配,按姓氏排序,然后是按姓氏排序的其余比赛)。

我想过把它变成2个查询集,然后只是组合列表,但我想知道是否有可能在1个查询中做到这一点,理想情况下坚持使用基本的django queryset API(而不是写一次性的查询)。有谁知道一种方法来做到这一点?

在此先感谢。

+0

也是你使用'django.contrib.auth.models.User'?如果是这样'first_name'和'last_name'是默认的字段名称。 – 2012-07-10 18:05:45

+0

不,这是一个不同的自定义模型。 – Chad 2012-07-10 19:25:57

+0

只是双重检查=) – 2012-07-10 19:28:37

回答

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 
+0

是的,我认为这是做到这一点的最佳方式。我可能最终会这样做,除非使用下面的Francis建议的itertools.chain,因为这应该比构建2个列表并将它们组合起来要快。 谢谢! – Chad 2012-07-10 20:16:24

+1

我很高兴我学到了itertools的链条技巧。但就你而言,如果你的限制真的是10,那不应该改变一件事情。查询将花费大部分时间,列表创建和连接时间将不重要。 – Etienne 2012-07-11 02:02:45

0

您可以通过多个属性进行订购。

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

所以你为了首先first所以你的对象按精确匹配和随后的比赛得到过滤器。然后你再次订购last根据姓氏排序。

+0

但是,不会按照名字排序整个列表吗?所以我最终会选择Sammy Rogers和Samuel Baker,对吧? – Chad 2012-07-10 05:51:25

+0

@Chad是的,它会的,但不应该是这样。恕我直言,我觉得这是预期的行为。 – Rohan 2012-07-10 06:09:33

+0

没有在这种情况下,这不是我们正在寻找,不幸的是。需要2个组,确切的名字匹配,然后是其他所有内容,每个组需要按姓氏排序。 – Chad 2012-07-10 06:17:05

1

我想你可能确实需要全文搜索才能完成。退房djang-sphinx

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

+0

感谢您的建议。在这种情况下,我不认为需要全文搜索,因为我有索引名称,所以可以在django ORM调用中做一个startswith。 – Chad 2012-07-10 20:19:03

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) 

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

+0

好的解决方案,虽然我最终接受了另一个,因为它考虑了10个项目的限制。(另外我认为在exclude()中有一个小的输入错误,我认为这应该是排除(pk__in = qs1))。 – Chad 2012-07-10 20:17:57

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