Django Q通过username,first_name和/或last_name查找用户

时间:2015-09-08 10:41:22

标签: python django logic

我想根据数据库中的usernamefirst_namelast_name字段找到用户。

到目前为止,我所拥有的是:

def _build_q_for_field(fieldname, wordlist):
    def _param(x):
        return {fieldname + '__icontains': x}
    return reduce(operator.or_, (Q(**_param(w)) for w in wordlist))

query_string = "Fabian M"
query_words = query_string.strip().split(' ')  # ['Fabian', 'M']

qs = User.objects.all()
qs = qs.filter(
    _build_q_for_field('username', query_words) |
    _build_q_for_field('first_name', query_words) |
    _build_q_for_field('last_name', query_words)
)

假设我们有以下非常简单的用户名:

  • 用户1:first_name = 'Fabian'last_name = 'Mueller'username = 'fmueller'
  • 用户2:first_name = 'Fabian'last_name = 'Schulze'username = 'fschulze'

上面的代码将返回两个对象,因为所有条件都通过OR连接,并且两个用户都有first_name = 'Fabian'。但是我想在结果集中只有用户Fabian Mueller,因为单词列表中的第二个单词(M)不包含在第二个结果中。

你有一个聪明的想法如何存档这个?使用AND而不是OR在过滤方法中组合Q在上面的示例中起作用,但如果单词列表只包含一个单词并且会立即排除用户名不匹配的结果,则会出现严重问题。上面的例子。

您是否有一个聪明的解决方案如何处理这个问题?提前谢谢!

1 个答案:

答案 0 :(得分:0)

如果在查询中引入了其他字段full_name,则可以解决该问题。似乎有必要这样做,因为否则必须考虑查询中的字段和单词的所有组合。可能的组合迅速爆发。

不幸的是Django< 1.8需要" raw" SQL语句(因此这取决于后端的数据库),而Django 1.8允许通过提供django.db.models.functions.Concatqueryset.annotate()来抽象问题。

query_db_fields = ['first_name', 'last_name', 'username']

if DJANGO_VERSION >= '1.8.0':
    from django.db.models.functions import Concat

    qs = qs\
         .annotate(full_name=Concat(*query_db_fields))\
         .filter(reduce(operator.and_, [Q(full_name__icontains=w) for w in query_words]))
else:
     # (DEPRECATED) Remove this part when support for Django < 1.8 is being dropped
    for w in query_words:
        qs = qs.extra(
             where=['lower(concat({fields:})) LIKE lower(%s)'.format(fields=','.join(query_db_fields))],
                    params=['%{}%'.format(w)]
        )