【问题标题】:Django: remove a filter condition from a querysetDjango:从查询集中删除过滤条件
【发布时间】: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'会更好,但这只是一个例子......)

【问题讨论】:

  • 你能展示一下代码示例吗?

标签: django django-models filtering django-queryset


【解决方案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 只是添加另一个过滤器,保留现有的过滤器。它不会恢复完整的查询集。
      • 哦,我明白了!我当时误解了这个问题。感谢您清除它:)
      猜你喜欢
      • 2019-01-08
      • 2020-08-22
      • 2019-04-15
      • 2011-09-29
      • 2019-03-16
      • 2018-10-02
      相关资源
      最近更新 更多