如何编辑查询集的过滤器列表

1 投票
2 回答
3513 浏览
提问于 2025-04-16 13:10

这个功能是在一个显示搜索结果的页面上使用的。在我的搜索表单中,有一些模型选择字段可以通过外键进行搜索。通常的工作流程是让我们的搜索越来越精确,因此为了去掉很多不相关的结果,我想移除那些如果其他搜索条件不变就不会返回结果的条目。

我使用一个对象查询集来限制一些下拉列表中的选项。我用这些下拉列表来根据外键过滤我的对象列表。

我用来过滤的函数参数是一个对象查询集,目前是这样的:

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 个回答

8

这里有一些小技巧。

你可以通过“重置”当前的过滤器来绕过这个问题,方法如下:

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
8

简短回答:

不,你不能这样做。

详细回答:

理论上,这可能在某种程度上是可行的,但绝对不推荐

(我没有完全研究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)。

困难的部分当然是如何移除过滤器,而不影响其他规则。我不打算提供解决方案,因为没有保证实现不会改变,从而使解决方案失效。我怀疑这是可能的,但这听起来像是只应该出于学术兴趣去做的事情,或者根本不去做。

撰写回答