【问题标题】:Django filter with OR condition using dict argument使用 dict 参数的带有 OR 条件的 Django 过滤器
【发布时间】:2016-06-30 19:17:40
【问题描述】:

我的 Django 应用程序上有一个函数,我在其中执行一些 Queryset 操作并将其结果设置为 Memcache。因为它是一个函数,所以它必须是通用的。因此,为了使其可重用,我将 dict 作为filterexclude 操作的参数传递。这是函数:

def cached_query(key, model, my_filter=None, exclude=None, order_by=None, sliced=50):
    """
    :param key: string used as key reference to store on Memcached
    :param model: model reference on which 'filter' will be called
    :param my_filter: dictionary containing the filter parameters (eg.: {'title': 'foo', 'category': 'bar'}
    :param sliced: integer limit of results from the query. The lower the better, since for some reason Django Memcached
        won't store thousands of entries in memory
    :param exclude: dictionary containing the exclude parameters (eg.: {'title': 'foo', 'category': 'bar'}
    :param order_by: tuple containing the list of fields upon which the model will be ordered.
    :return: list of models. Not a QuerySet, since it was sliced.
    """
    result = cache.get(key, None)
    if not result:
        if my_filter:
            result = model.objects.filter(**my_filter)
        if exclude:
            result = result.exclude(**exclude)
        if order_by:
            result = result.order_by(*order_by)
        else:
            result = model.objects.all()
        result = result[:sliced]
        cache.set(key, result, cache_timeout)
    return result

如果我使用像{'title': 'foo', 'name': 'bar'} 这样的简单字典过滤查询集,它会很好地工作。然而,情况并非总是如此。对于需要OR 条件的更复杂查询,我需要使用django.db.models.Q 实用程序执行过滤器。

那么,如何将这些参数作为字典传递给过滤器。有什么办法吗?

【问题讨论】:

    标签: python django


    【解决方案1】:

    您可以使用按位| 运算符。

    my_filter = Q()
    
    # Or the Q object with the ones remaining in the list
    my_or_filters = {'some_field__gte':3.5, 'another_field':'Dick Perch'}
    
    for item in my_or_filters:
        my_filter |= Q(**{item:my_or_filters[item]})
    
    model.objects.filter(my_filter)
    # unpacks to model.objects.filter(Q(some_field__gte=3.5) | Q(another_field='Dick Perch'))
    

    考虑到这一点,您可能希望将存储在my_filter 中的所有查询加载到Q 对象中。然后,您可以通过上面相同的方法使用按位& 加入所有非 OR 查询: my_filter &= ...

    【讨论】:

    • 糟糕,有引号中的项目。尝试如上。
    【解决方案2】:

    您可以将字典重组为单个键值字典列表,并在 Q 表达式中对每个 dict 使用解包,如下所示:

    from functools import reduce
    import operator
    
    from django.db.models import Q
    
    # your dict is my_filter
    q = model.objects.filter(reduce(operator.or_, 
                                    (Q(**d) for d in [dict([i]) for i in my_filter.items()])))
    

    or_ 上的reduce 在 OR 上加入 Q 表达式。

    您还可以使用生成器表达式,其中包含listdicts:

    q = model.objects.filter(reduce(operator.or_, 
                                    (Q(**d) for d in (dict([i]) for i in my_filter.items()))))
    

    【讨论】:

      【解决方案3】:

      根据@Moses Koledoye 的回答,我可以解决这个问题。这就是我的函数现在的样子:

       cached_query(key, model, my_filter=None, or_filter={}, exclude=None, order_by=None, sliced=50):
          """
          :param key: string used as key reference to store on Memcached
          :param model: model reference on which 'filter' will be called
          :param my_filter: dictionary containing the filter parameters (eg.: {'title': 'foo', 'category': 'bar'}
          :param or_filter: dictionary containing the filter parameters (eg.: {'title': 'foo', 'category': 'bar'}
          :param sliced: integer limit of results from the query. The lower the better, since for some reason Django Memcached
              won't store thousands of entries in memory
          :param exclude: dictionary containing the exclude parameters (eg.: {'title': 'foo', 'category': 'bar'}
          :param order_by: tuple containing the list of fields upon which the model will be ordered.
          :return: list of models. Not a QuerySet, since it was sliced.
          """
      
          result = cache.get(key, None)
          if not result:
              result = model.objects.all()
              if my_filter:
                  result = model.objects.filter(**my_filter)
              if or_filter:
                  reduced_filter = reduce(operator.or_, (Q(**d) for d in [dict([i]) for i in or_filter.items()]))
                  result = result.filter(reduced_filter)
              if exclude:
                  result = result.exclude(**exclude)
              if order_by:
                  result = result.order_by(*order_by)
              result = result[:sliced]
              cache.set(key, result, cache_timeout)
          return result
      

      【讨论】:

        猜你喜欢
        • 2018-03-23
        • 1970-01-01
        • 1970-01-01
        • 2023-01-24
        • 2011-10-03
        • 1970-01-01
        • 2016-05-26
        • 1970-01-01
        • 2019-10-10
        相关资源
        最近更新 更多