我想知道是否有办法使用property()
使用动态生成的python属性在查询集上使用Django的filter()。我有first_name
和last_name
的每个用户,我想根据其连接的名称first_name last_name
进行过滤。 (这背后的原因是,当我进行自动填充时,我会搜索查询是否与名字,姓氏或部分串联匹配。我希望John S
匹配John Smith
,例如。< / p>
我创建了name
:
def _get_name(self):
return self.first_name + " " + self.last_name
name = property(_get_name)
这样我就可以调用user.name
来获取连接名称。
但是,如果我尝试User.objects.filter(name__istartswith=query)
,我会收到错误Cannot resolve keyword 'name' into field.
关于如何做到这一点的任何想法?我是否必须在数据库中创建另一个字段来存储全名?
答案 0 :(得分:13)
接受的答案并非完全正确。
在许多情况下,您可以在模型管理器中将get()
覆盖为关键字参数的pop
动态属性,然后将要查询的实际属性添加到kwargs
关键字中参数字典。请务必返回super
,以便定期get()
次调用返回预期结果。
我只是粘贴我自己的解决方案,但对于__startswith
和其他条件查询,您可以向split
添加一些逻辑双下划线并正确处理。
以下是我的解决方法,允许通过动态属性进行查询:
class BorrowerManager(models.Manager):
def get(self, *args, **kwargs):
full_name = kwargs.pop('full_name', None)
# Override #1) Query by dynamic property 'full_name'
if full_name:
names = full_name_to_dict(full_name)
kwargs = dict(kwargs.items() + names.items())
return super(BorrowerManager, self).get(*args, **kwargs)
在models.py中:
class Borrower(models.Model):
objects = BorrowerManager()
first_name = models.CharField(null=False, max_length=30)
middle_name = models.CharField(null=True, max_length=30)
last_name = models.CharField(null=False, max_length=30)
created = models.DateField(auto_now_add=True)
在utils.py中(为了上下文):
def full_name_to_dict(full_name):
ret = dict()
values = full_name.split(' ')
if len(values) == 1:
raise NotImplementedError("Not enough names to unpack from full_name")
elif len(values) == 2:
ret['first_name'] = values[0]
ret['middle_name'] = None
ret['last_name'] = values[1]
return ret
elif len(values) >= 3:
ret['first_name'] = values[0]
ret['middle_name'] = values[1:len(values)-1]
ret['last_name'] = values[len(values)-1]
return ret
raise NotImplementedError("Error unpacking full_name to first, middle, last names")
答案 1 :(得分:9)
filter()
在数据库级别上运行(它实际上是在编写SQL),因此不可能将它用于基于python代码(dynamic property in your question)
的任何查询。
这是该部门许多其他答案的答案:)
答案 2 :(得分:3)
我有类似的问题,正在寻找解决方案。理所当然地认为搜索引擎是最好的选择(例如django-haystack
和Elasticsearch
),这就是我如何仅使用Django ORM为您的需求实现一些代码(您可以替换{{1} } icontains
}:
istartswith
就我而言,我不知道用户是否会插入“from django.db.models import Value
from django.db.models.functions import Concat
queryset = User.objects.annotate(full_name=Concat('first_name', Value(' '), 'last_name')
return queryset.filter(full_name__icontains=value)
first_name
”或反之,所以我使用了以下代码。
last_name
使用Django&lt; 1.8,您可能需要使用SQL from django.db.models import Q, Value
from django.db.models.functions import Concat
queryset = User.objects.annotate(first_last=Concat('first_name', Value(' '), 'last_name'), last_first=Concat('last_name', Value(' '), 'first_name'))
return queryset.filter(Q(first_last__icontains=value) | Q(last_first__icontains=value))
函数求助extra
,如下所示:
CONCAT
答案 3 :(得分:0)
认为在django中不可能过滤不作为数据库字段存在的属性,但是你可以做些什么来制作很酷的自动完成搜索是这样的:
if ' ' in query:
query = query.split()
search_results = list(chain(User.objects.filter(first_name__icontains=query[0],last_name__icontains=query[1]),
User.objects.filter(first_name__icontains=query[1],last_name__icontains=query[0])))
else:
search_results = User.objects.filter(Q(first_name__icontains=query)| Q(last_name__icontains=query))
此代码为您的系统用户提供了开始输入名字或姓氏的灵活性,用户会感谢您允许这样做。