Django:从查询集中删除过滤条件

Posted

技术标签:

【中文标题】Django:从查询集中删除过滤条件【英文标题】:Django: remove a filter condition from a queryset 【发布时间】:2011-06-09 00:59:54 【问题描述】:

我有一个第三方函数,它给我一个过滤的查询集(例如,'valid'=True 的记录),但我想删除一个特定条件(例如,让所有记录,包括有效和无效)。

有没有办法将过滤条件删除到已过滤的查询集中?

例如

only_valid = MyModel.objects.filter(valid=True)
all_records = only_valid.**remove_filter**('valid')

(我知道在'only_valid'之前定义'all_records'会更好,但这只是一个例子......)

【问题讨论】:

你能展示一下代码示例吗? 【参考方案1】:

虽然没有使用过滤器符号的官方方法来做到这一点,但您可以使用Q-notation 轻松做到这一点。 例如,如果您确保第三方函数返回的是 Q 对象,而不是过滤后的 QuerySet,则可以执行以下操作:

q = ThirdParty()
q = q | Q(valid=False)

生成的 SQL 条件将使用 OR 运算符连接。

【讨论】:

您将需要访问第三方代码,但是是的,这是一个很好的解决方案。 +1【参考方案2】:

使用此功能

from django.db.models import Q

def remove_filter(lookup, queryset):
    """
    Remove filter lookup in queryset
    ```
    >>> queryset = User.objects.filter(email='user@gmail.com')
    >>> queryset.count()
    1
    >>> remove_filter('email', queryset)
    >>> queryset.count()
    1000
    ```
    """
    query = queryset.query
    q = Q(**lookup: None)
    clause, _ = self._add_q(q, self.used_aliases)

    def filter_lookups(child):
        return child.lhs.target != clause.children[0].lhs.target

    query.where.children = list(filter(filter_lookups, query.where.children))

【讨论】:

自己应该被查询【参考方案3】:

来自the docs:

每次优化 QuerySet 时,都会得到一个全新的 QuerySet,它绝不会绑定到之前的 QuerySet。每个细化都会创建一个单独且不同的 QuerySet,可以存储、使用和重用。

因此,我怀疑是否有一种标准的方法可以做到这一点。你可以dig into the code,看看filter() 做了什么,然后尝试一下。如果这没有帮助,我的假设是,你运气不好,需要自己重新构建查询。

【讨论】:

【参考方案4】:

这是我在类似案例中所做的。

all_records = MyModel.objects.all()
only_valid = MyModel.objects.filter(valid=True)
only_valid.original = all_records

...

all_records = only_valid.original

显然,这也会清除任何其他过滤器,因此并非适用于所有情况。

【讨论】:

【参考方案5】:
original_query_set = MyModel.objects.filter(**conditions)
model_class = orginal_query_set.model
new_query_set = model_class.objects.filter(**new_conditions)

您可以使用 QuerySet 上的 .model 属性来获取模型类,然后使用模型类创建一个全新的 QuerySet。

【讨论】:

请考虑解释为什么您的代码解决了操作的问题【参考方案6】:

感谢您让我检查 Boldewyn 的源代码。因此,似乎在 filter() 下方有一个具有相同参数的新方法...称为 exclude() (从提交 mini-hash ef6c680 开始,当它丢失行号时)

返回一个新的 QuerySet 实例,用 NOT (args) ANDed 到现有集合。

所以,回答原来的问题:

only_valid = MyModel.objects.filter(valid=True)
filtered_results = only_valid.exclude(the_condition_to_remove=True)

【讨论】:

exclude 只是添加另一个过滤器,保留现有的过滤器。它不会恢复完整的查询集。 哦,我明白了!我当时误解了这个问题。感谢您清除它:)

以上是关于Django:从查询集中删除过滤条件的主要内容,如果未能解决你的问题,请参考以下文章

Django:在查询集中过滤 get_foo_display

在 Django 查询集中动态定义过滤器参数

使用 ORM Django 过滤新更新的查询集返回空查询集

基于 Django 查询集中外键字段的 .count() 进行过滤

Django中模型

Django添加Q过滤器以查询相关对象存在时,条件查询