【问题标题】:Django Not in for Related FieldsDjango 不在相关领域
【发布时间】:2015-08-12 20:42:05
【问题描述】:

当我有相关字段时,我将如何在 Django 中进行not in 过滤器?

class Publisher(Model):
  pass

class Author(Model):
  name = models.CharField(...)
  publisher = models.ForeignKey(Publisher)

我会写:

Publisher.objects.filter(author__name__in=XXX)

这不起作用:

Publisher.objects.filter(author__name__not_in=XXX)

虽然Django equivalent of SQL not in 声称您可以使用exclude,但这是不正确的:

Publisher.objects.exclude(author__name__in=XXX)

XXX 是一个名称列表。

澄清我想得到什么:我想找到所有作者不在该列表中的出版商。注意:这些出版商可能在该列表中也有作者。

假设我有两个出版商 A、B 和以下出版商的作者:

A: Alex, Bob, Greg
B: Alex, Greg

xxx 是 ['Alex', 'Greg']

我想查找作者不在该列表中的任何出版商。在这种情况下,应该返回 A,因为 A 的作者 Bob 不在该列表中。

【问题讨论】:

    标签: django django-queryset django-orm django-filter


    【解决方案1】:

    检查exclude() 函数。它是 filter() 函数的逆函数,但完全符合您的要求。

    注意你也可以chain functions together:

    >>> Entry.objects.filter(
    ...     headline__startswith='What'
    ... ).exclude(
    ...     pub_date__gte=datetime.date.today()
    ... ).filter(
    ...     pub_date__gte=datetime(2005, 1, 30)
    ... )
    

    【讨论】:

    • 我很确定在这种情况下我不想要过滤器的倒数。我想找到所有作者不在该列表中的出版商。注意:这些出版商可能在该列表中也有作者。
    【解决方案2】:

    什么是XXX,如果是list,您应该可以在查询中使用exclude

    Publisher.objects.exclude(author__name__in=XXX)  # Remember XXX must be a list
    

    如果是string,你应该使用:

    Publisher.objects.exclude(author__name__contains=XXX)
    

    或者如果大小写无关紧要:

    Publisher.objects.exclude(author__name__icontains=XXX)
    

    【讨论】:

    • 更新显示XXX 是一个列表。每*.com/questions/30538768/… exclude 不是我想要的。
    • 请明确exclude 不起作用,或者它没有返回您期望的结果?
    • 它没有返回我想要的。我不想否定整个过滤器,我想否定 in。
    • 你能举个例子,包括数据、XXX 值和你的期望吗?
    • 更新了更丰富的例子。
    【解决方案3】:

    可以使用以下内容:

    Publisher.author.filter(author__in=Author.objects.exclude(name__in=xxx))
    

    潜在的巨大子查询会带来性能成本,尤其是在不能很好地处理子查询的 MySQL 上。

    另一种方法是实现 NOT IN custom lookup,这从 1.7 开始就可以实现。它看起来像这样:

    class NotInLookup(Lookup):
        lookup_name = 'not_in'
    
        def as_sql(self, compiler, connection):
            lhs, lhs_params = self.process_lhs(compiler, connection)
            rhs, rhs_params = self.process_rhs(compiler, connection)
            params = lhs_params + rhs_params
            return '%s NOT IN (%s)' % (lhs, rhs), params
    

    您必须调查文档以及可能的 source code of IN,才能找到编写此查找的正确方法。可能有兴趣将它添加到核心中,所以如果你让它工作,请随时将它放在邮件列表中https://groups.google.com/forum/#!forum/django-developers

    【讨论】:

      【解决方案4】:

      看起来Q objects 很适合这个查询。

      您可以先建立查询参数。

      import operator
      
      query = reduce(
          lambda x, y: operator.or_(Q(author__name=x), Q(author__name=y)),
          XXX
      )
      # query would be something like Q(author__name='Alex') | Q(author__name='Greg')
      
      Publisher.objects.exclude(query)
      

      使用完全匹配的示例,如果您要在 CharField 中搜索 ,则必须使用 containsicontains。只需使用 Q(author__name__icontains=n) 更新 Q 对象。

      【讨论】:

      • 如果xxx是包含字符串的变量名,则不需要''
      • 是的,你是对的。在问题的最后一次更新之前不清楚 XXX 是什么,所以我试图通过说它必须是一个字符串来说明它。