【问题标题】:django conditionally filtering objectsdjango 有条件地过滤对象
【发布时间】:2012-09-29 08:34:18
【问题描述】:

我想使用一组过滤器从我的数据库中检索一堆行。

我想知道条件过滤器是否适用于 django。也就是说,“如果变量不是无则过滤,否则不应用过滤”。

类似这样的:

user = User.objects.get(pk=1)
category = Category.objects.get(pk=1)
todays_items = Item.objects.filter(user=user, date=now()).conditional_filter(category=category))

我想做的是仅在类别不是无时应用类别过滤器。

如果类别为无(意味着它没有在请求对象中给出),则根本不会应用此过滤器。这将为我省去一堆“if-elif-else”的情况。

有没有办法做到这一点?

【问题讨论】:

    标签: django


    【解决方案1】:

    您可以链接查询:

    user = User.objects.get(pk=1)
    category = Category.objects.get(pk=1)
    qs = Item.objects.filter(user=user, date=now())
    if category:
        qs = qs.filter(category=category)
    

    由于查询集是惰性执行的,只有在显示项目时才会发生 DB 命中。

    【讨论】:

    • 令人满意的简单答案
    • 这是一个可读且易于实现的答案,并且因为查询集是惰性求值的,所以性能也很好。
    • 您需要qs = qs.filter(...) 还是只需在现有的QuerySet 上调用filter(...) 就足够了吗?
    【解决方案2】:

    它们是解决您问题的几种方法。一种方法是玩Complex lookups with Q objects

    from django.db.models import Q
    
    user = User.objects.get(pk=1)
    category = Category.objects.get(pk=1)
    
    f1 = Q( user=user, date=now() )
    f_cat_is_none = Q( category__isnull = True )
    f_cat_is_not_none = Q( category=category )
    
    todays_items = Item.objects.filter( f1 & ( f_cat_is_none | f_cat_is_not_none ) )
    

    如果这是您要查找的查询,我在您的回答中没有正确理解,但是,通过此示例,您可以轻松编写自己的查询。

    已编辑应有的 OP 评论

    category__isnull == True 表示,在数据库中,该项目没有关联的类别。 也许您正在寻找的查询是:

    from django.db.models import Q
    
    user_pk = 1
    category_pk = 1  #some times None
    
    f = Q( user__pk = user_pk, date=now() )
    if category_pk is not None:
      f &= Q( category__pk = category_pk )
    
    todays_items = Item.objects.filter( f  )
    

    这只是一个代码示例,适合您的要求。注意单_和双__

    【讨论】:

    • 谢谢,我不明白的是category__isnull = True。我的所有对象的类别字段中都没有 NULL。这使它始终为 False。
    • @xpanta,在回答中进行了解释。
    • @danihp,对不起,我还是不太明白 __isnull=True。它是否适用于这种情况:包含“类别”的表单,用户填写并提交表单。如果用户没有输入“类别”,则此值为“”。如果我遵循您的第一个示例,那么在没有“类别”约束的情况下过滤数据库?如果我的理解正确,请纠正我。提前致谢。
    • @Elena,抱歉,不确定您要什么。第一个样本上的__isnull 表示没有类别的项目。
    【解决方案3】:

    嗯,这是一个相当老的问题,但是对于那些想在一行上进行条件过滤的人,这是我的方法(顺便说一句,以下代码可能可以用更通用的方式编写):

    from django.db.models import Q
    
    def conditional_category_filter(category):
        if category != None:
            return Q(category=category)
        else:
            return Q() #Dummy filter
    
    user = User.objects.get(pk=1)
    category = Category.objects.get(pk=1)
    todays_items = Item.objects.filter(conditional_category_filter(category), user=user, date=now())
    

    您唯一需要注意的是在 user=user 等关键字参数之前使用 conditional_category_filter(category) 调用。例如下面的代码会抛出一个错误:

    todays_items = Item.objects.filter(user=user, date=now(), conditional_category_filter(category))
    

    【讨论】:

      【解决方案4】:

      继续@iuysal 回答:

      要使其通用,您还需要将密钥作为参数传递,为此您需要传递字典,这是我的做法:

      像这样创建你的字典:

      filters = {'filter1': 'value1', 'filter2__startswith': 'valu', ...}
      

      然后将其传递给您的项目过滤器,如下所示:

      Item.objects.filter(*[Q(**{k: v}) for k, v in filters.items() if v], filter3='value3')
      

      我拥有的第一个不那么神秘的版本:

      def _filter(filters):
          filters = []
          for k, v in n.items():
              if v:
                  filters.append(Q(**{k: v}))
          return filters
      
      filters = _filter({'name': name})
      return Item.objects.filter(*filters)
      

      解包说明:我们想把Q (queries)作为argsobjects.filter作为args,而我们想把kwargsQ()

      我现在正在生产中(我将只修改过滤器名称,因为它很敏感):

      def get_queryset(self):
          filter1 = self.request.GET.get('filter1 ', '')
          filter2__startswith = self.request.GET.get('filter2_prefix ', '')
      
          def filters_to_Qs(filters):
              return [Q(**{k: v}) for k, v in filters.items() if v]
      
          filters = {'filter1': filter1 ,
                     'filter2__startswith': filter2__startswith }
      
          return Order.objects.filter(*filters_to_Qs(filters))
      

      【讨论】:

        【解决方案5】:
        from django.db.models import Q
        
        qs = Users.objects.filter(
                            p_id=parent_id,
                            status=True
                        ).all()
        
        if user_id>0:
            qs = qs.filter( ~Q(id=user_id) )
        

        qs我们会得到过滤后的结果

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-09-30
          • 2013-02-27
          • 2019-01-08
          • 2017-01-07
          • 2021-07-05
          • 1970-01-01
          相关资源
          最近更新 更多