如何编辑查询集的过滤器列表
这个功能是在一个显示搜索结果的页面上使用的。在我的搜索表单中,有一些模型选择字段可以通过外键进行搜索。通常的工作流程是让我们的搜索越来越精确,因此为了去掉很多不相关的结果,我想移除那些如果其他搜索条件不变就不会返回结果的条目。
我使用一个对象查询集来限制一些下拉列表中的选项。我用这些下拉列表来根据外键过滤我的对象列表。
我用来过滤的函数参数是一个对象查询集,目前是这样的:
class MySearchForm(Form):
things = ModelChoiceField(queryset=models.Thing.objects.none())
def __init__(self, *args, **kwargs):
my_objects_queryset = kwargs.pop('my_objects_queryset',models.Thing.objects.all())
super(MySearchForm, self).__init__(*args, **kwargs)
self.fields['things'].queryset = \
models.Thing.objects.filter(object__in=my_objects_queryset).distinct()
我的问题是如何从现有的查询集中移除一个“where条件”。在这里,我想从我的对象查询集中移除那些通过 thing = ForeignKey(models.Thing)
进行过滤的条件。
这可能吗?
我想要的就是一种方法,可以列出我们查询集的所有过滤条件,并能随时编辑或删除它们。
2 个回答
这里有一些小技巧。
你可以通过“重置”当前的过滤器来绕过这个问题,方法如下:
from django.db.models.sql.where import WhereNode
qs = YourModel.objects.filter(column=1).all()
qs.query.where = WhereNode() # resets current filters
qs.all() # now you can apply new filters
简短回答:
不,你不能这样做。
详细回答:
理论上,这可能在某种程度上是可行的,但绝对不推荐。
(我没有完全研究django的源代码,以下内容只是简单浏览了一下调用关系。)
从QuerySet的源代码来看,filter()
和exclude()
方法返回的是原始查询集的一个克隆,并在其上添加了新的规则(使用clone.query.add_q()
)。
clone.query
是一个表示SQL查询的对象,而add_q()方法则将新规则添加到clone.query.where
,这基本上是一个树的根节点。
新节点如何添加到树中,取决于这个规则是用AND
还是OR
连接。这一点很重要,因为它会影响最终生成的SQL查询的正确性。
所以,要列出分配给查询集的过滤器,“简单地”需要理解queryset.query.where
树是如何表示的(见django.utils.tree
)。
困难的部分当然是如何移除过滤器,而不影响其他规则。我不打算提供解决方案,因为没有保证实现不会改变,从而使解决方案失效。我怀疑这是可能的,但这听起来像是只应该出于学术兴趣去做的事情,或者根本不去做。