给定一个类:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=20)
是否有可能,如果有的话,如何拥有一个基于动态参数进行过滤的 QuerySet?例如:
# 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`.
Python的参数扩展可以用来解决这个问题:
kwargs = {
'{0}__{1}'.format('name', 'startswith'): 'A',
'{0}__{1}'.format('name', 'endswith'): 'Z'
}
Person.objects.filter(**kwargs)
这是一个非常常见和有用的 Python 习惯用法。
一个简化的例子:
在 Django 调查应用程序中,我想要一个显示注册用户的 HTML 选择列表。但是因为我们有 5000 个注册用户,所以我需要一种方法来根据查询条件(例如完成某个研讨会的人)过滤该列表。为了使调查元素可重复使用,我需要创建调查问题的人能够将这些标准附加到该问题(不想将查询硬编码到应用程序中)。
我想出的解决方案不是 100% 用户友好(需要技术人员的帮助来创建查询),但它确实解决了问题。创建问题时,编辑器可以在自定义字段中输入字典,例如:
{'is_staff':True,'last_name__startswith':'A',}
该字符串存储在数据库中。在视图代码中,它以 self.question.custom_query
的形式返回。它的值是一个 看起来 像字典的字符串。我们使用 eval() 将其转回 real 字典,然后使用 **kwargs 将其填充到查询集中:
kwargs = eval(self.question.custom_query)
user_list = User.objects.filter(**kwargs).order_by("last_name")
eval()
是个坏主意,即使您完全信任您的用户。 JSON 字段在这里会是一个更好的主意。
Django.db.models.Q 正是您想要的 Django 方式。
Q(**filters)
,如果您想动态构建 Q 对象,可以将它们放在列表中并使用 .filter(*q_objects)
,或者使用按位运算符组合 Q 对象。
此外,为了扩展先前对进一步代码元素提出一些请求的答案,我正在添加一些我在 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
一个非常复杂的搜索表单通常表明一个更简单的模型正在试图挖掘它的出路。
确切地说,您希望如何获得列名和操作的值?您从哪里获得 'name'
和 'startswith'
的值?
filter_by = '%s__%s' % ('name', 'startswith')
“搜索”表格?你要——什么? -- 从名字列表中选择名字?从操作列表中选择操作?虽然是开放式的,但大多数人发现这令人困惑且难以使用。有多少列有这样的过滤器? 6? 12? 18?一些?复杂的选择列表没有意义。一些字段和一些 if 语句是有意义的。大量?你的模型听起来不对。听起来“字段”实际上是另一个表中行的键,而不是列。特定的过滤器按钮。等等……这就是 Django 管理员的工作方式。特定的过滤器变成了按钮。并且适用与上述相同的分析。一些过滤器是有意义的。大量的过滤器通常意味着一种第一范式违例。
许多类似的字段通常意味着应该有更多的行和更少的字段。
不定期副业成功案例分享