Django Q 通过用户名查找用户,first_name and/or last_name
Django Q find user by username, first_name and/or last_name
我想根据数据库中的 username
、first_name
和 last_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'
上面的代码将 return 这两个对象,因为所有条件都通过 OR 连接并且两个用户都有 first_name = 'Fabian'
。但我想在结果集中只有用户 Fabian Mueller,因为单词列表中的第二个单词 (M
) 不包含在第二个结果中。
你知道如何存档吗?在上面的示例中使用 AND 而不是 OR 组合过滤器方法中的 Q 确实有效,但如果单词列表仅包含一个单词则会出现严重问题,并且会立即排除用户名不匹配的结果,例如上面的例子。
你有什么聪明的办法来处理这个问题吗?提前致谢!
如果在查询中引入额外的字段full_name
,问题就可以解决。这样做似乎是必要的,因为否则必须考虑查询中所有字段和词的组合。可能的组合迅速爆炸。
不幸的是,Django < 1.8 需要一个 "raw" SQL 语句(因此这取决于后端的数据库),而 Django 1.8 允许通过提供 django.db.models.functions.Concat
和 queryset.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)]
)
我想根据数据库中的 username
、first_name
和 last_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'
上面的代码将 return 这两个对象,因为所有条件都通过 OR 连接并且两个用户都有 first_name = 'Fabian'
。但我想在结果集中只有用户 Fabian Mueller,因为单词列表中的第二个单词 (M
) 不包含在第二个结果中。
你知道如何存档吗?在上面的示例中使用 AND 而不是 OR 组合过滤器方法中的 Q 确实有效,但如果单词列表仅包含一个单词则会出现严重问题,并且会立即排除用户名不匹配的结果,例如上面的例子。
你有什么聪明的办法来处理这个问题吗?提前致谢!
如果在查询中引入额外的字段full_name
,问题就可以解决。这样做似乎是必要的,因为否则必须考虑查询中所有字段和词的组合。可能的组合迅速爆炸。
不幸的是,Django < 1.8 需要一个 "raw" SQL 语句(因此这取决于后端的数据库),而 Django 1.8 允许通过提供 django.db.models.functions.Concat
和 queryset.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)]
)