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


160

给定一个班级:

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`.

Answers:


310

Python的参数扩展可用于解决此问题:

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

Person.objects.filter(**kwargs)

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


6
只是需要快速了解一下:确保kwarg中的字符串的类型不是str,而是unicode,否则filter()会发牢骚。
史蒂夫·贾利姆

1
@santiagobasulto也称为参数打包/解压缩及其变体。
Daniel Naab 2011年

7
很好,很好,很好
奥斯卡·梅德罗斯

5
@DanielNaab,但这仅适用于在AND条件过滤下工作的kwarg,或OR条件的任何替代方法。
Prateek099

3
@prateek,您可以始终使用Q对象:stackoverflow.com/questions/13076822/…–
deecodameeko

6

一个简化的例子:

在Django调查应用程序中,我想要一个显示注册用户的HTML选择列表。但是,因为我们有5000个注册用户,所以我需要一种基于查询条件(例如仅完成某个研讨会的人员)过滤该列表的方法。为了使调查元素可重复使用,我需要创建调查问题的人能够将那些条件附加到该问题上(不想将查询硬编码到应用程序中)。

我想出的解决方案并非100%用户友好(需要技术人员的帮助才能创建查询),但确实可以解决问题。创建问题时,编辑者可以在自定义字段中输入字典,例如:

{'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")   

我想知道创建自定义ModelField / FormField / WidgetField会实现什么行为,以允许用户在GUI端基本上“构建”查询,从不查看实际文本,而是使用界面来执行操作这样做。听起来像一个整洁的项目……
T. Stone

1
T. Stone-我想如果需要查询的模型很简单,那么以一种简单的方式构建这样的工具会很容易,但是要彻底暴露所有可能的选择却很难,特别是如果模型是复杂。
黑客

5
-1调用eval()用户导入是一个坏主意,即使您完全信任用户也是如此。在这里,JSON字段将是一个更好的主意。
约翰·卡特

5

Django.db.models.Q正是您想要的Django方式。


7
您(或某人)能否提供使用动态字段名称时如何使用Q对象的示例?
jackdbernier 2014年

3
这与Daniel Naab的答案相同。唯一的区别是,您将参数传递给Q对象构造函数。Q(**filters),如果要动态构建Q对象,可以将它们放在列表中并使用.filter(*q_objects),也可以使用按位运算符组合Q对象。
威尔小号

5
这个答案实际上应该包括使用Q解决OP问题的示例。
pdoherty926

-2

一个非常复杂的搜索表通常表明一个更简单的模型正在尝试挖掘出它的出路。

您究竟希望如何获得列名和操作的值?您从哪里获得'name'an 的值'startswith'

 filter_by = '%s__%s' % ('name', 'startswith')
  1. “搜索”表格?您要-什么?-从名称列表中选择名称?从操作列表中选择操作?尽管开放式,但大多数人都觉得这令人困惑且难以使用。

    有多少列具有此类过滤器?6吗 12吗 18吗

    • 一些?复杂的选择列表没有任何意义。一些字段和一些if语句是有意义的。
    • 大量?您的模型听起来不正确。听起来“字段”实际上是另一个表中的一行的键,而不是列的键。
  2. 特定的过滤器按钮。等等...这就是Django管理员的工作方式。特定的过滤器变成按钮。与上述分析相同。一些过滤器很有意义。大量的过滤器通常意味着一种违反第一范式的行为。

许多相似的字段通常意味着应该有更多的行和更少的字段。


9
顺便说一句,在不了解设计的情况下提出建议是很冒昧的。要“简单地实现”此应用程序将获得符合要求的天文功能(> 200个应用程序^ 21 foos)。您正在阅读示例的目的和意图;你不应该。:)
Brian M. Hunt

2
我遇到了很多人,他们认为,只有(a)更加通用并且(b)像他们想象的那样工作,才能解决他们的问题。这样就无尽的挫败感,因为事情并非他们想象的那样。我已经看到太多的失败源于“修复框架”。
S.Lott

2
根据Daniel的回应,事情按预期和期望进行。我的问题是关于语法,而不是设计。如果我有时间写出设计,那我一定会做的。我确定您的意见会有所帮助,但这不是一个实际的选择。
Brian M. Hunt

8
S.Lott,您的答案甚至无法远程回答这个问题。如果您不知道答案,请不要回答任何问题。如果您对设计的知识绝对为零,请不要主动提出设计建议!
slypete

2
@slypete:如果对设计进行更改可以消除问题,则可以解决问题。基于不良设计继续前进的过程比必要的过程更加昂贵和复杂。解决根本原因问题比解决其他由不良设计决策引起的问题要好。抱歉,您不喜欢根本原因分析。但是,当事情真的很困难时,通常意味着您尝试的是错误的事情。
S.Lott
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.