在Django中,如何使用动态字段查找过滤QuerySet?

191 投票
6 回答
78179 浏览
提问于 2025-04-11 18:19

给定一个类:

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=20)

有没有可能,或者说如果可能的话,如何创建一个可以根据动态参数进行过滤的查询集?比如说:

 # Instead of:
 Person.objects.filter(name__startswith='B')
 # ... and:
 Person.objects.filter(name__endswith='B')

 # ... is there some way, given:
 filter_by = '{0}__{1}'.format('name', 'startswith')
 filter_value = 'B'

 # ... that you can run the equivalent of this?
 Person.objects.filter(filter_by=filter_value)
 # ... which will throw an exception, since `filter_by` is not
 # an attribute of `Person`.

6 个回答

7

为了进一步补充之前的回答,我在这里添加了一些我在代码中使用的有效代码,主要是和Q有关的。假设在我的请求中,可以选择是否对某些字段进行过滤,比如:

publisher_id
date_from
date_until

这些字段可以出现在查询中,但也可能会被省略。

这是我如何根据这些字段在一个聚合查询中构建过滤器的,注意这个查询在初始执行后不能再进行进一步的过滤:

# prepare filters to apply to queryset
filters = {}
if publisher_id:
    filters['publisher_id'] = publisher_id
if date_from:
    filters['metric_date__gte'] = date_from
if date_until:
    filters['metric_date__lte'] = date_until

filter_q = Q(**filters)

queryset = Something.objects.filter(filter_q)...

希望这对你有帮助,因为我花了不少时间来研究这个问题。

编辑:

另外一个好处是,你也可以使用列表。对于之前的例子,如果你有一个叫做publisher_ids的列表,而不是publisher_id,那么你可以使用这段代码:

if publisher_ids:
    filters['publisher_id__in'] = publisher_ids
9

这是一个简化的例子:

在一个Django的调查应用中,我想要一个HTML下拉列表,显示所有注册用户。但是因为我们有5000个注册用户,我需要一种方法来根据查询条件过滤这个列表(比如只显示那些完成了某个工作坊的人)。为了让这个调查元素可以重复使用,我需要让创建调查问题的人能够把这些条件附加到问题上(我不想把查询写死在应用里)。

我想到的解决方案虽然不是特别用户友好(需要技术人员的帮助来创建查询),但确实解决了问题。在创建问题时,编辑者可以在一个自定义字段中输入一个字典,例如:

{'is_staff':True,'last_name__startswith':'A',}

这个字符串会存储在数据库中。在视图代码中,它会以self.question.custom_query的形式返回。这个值是一个看起来像字典的字符串。我们使用eval()把它转换回一个真正的字典,然后用**kwargs把它放进查询集中:

kwargs = eval(self.question.custom_query)
user_list = User.objects.filter(**kwargs).order_by("last_name")   
366

Python中的参数扩展可以用来解决这个问题:

kwargs = {
    '{0}__{1}'.format('name', 'startswith'): 'A',
    '{0}__{1}'.format('name', 'endswith'): 'Z'
}

Person.objects.filter(**kwargs)

这是一个非常常见且有用的Python写法。

撰写回答