【问题标题】:How can I filter __in django ORM by records list?如何按记录列表过滤 __in django ORM?
【发布时间】:2021-07-09 12:46:31
【问题描述】:

我希望这样的查询:

SELECT field1, field2, field3
FROM mytable
WHERE (field1, field2) NOT IN(
    (1, 1), 
    (2, 2)
);

django==1.9.13

我目前的解决方案是:

qs = MyTable.objects.all()
for (field1, field2) in data:  # List[Tuple[int, int]]
    qs = qs.exclude(field1=field1, field2=field2)

【问题讨论】:

  • 老实说,您的解决方案并没有那么糟糕,因为 Django 查询集是惰性的,并且在评估之前不会执行。它不会在您的 for 循环的每次迭代中访问数据库。

标签: python django python-2.7


【解决方案1】:

如果您确实需要,您可以使用 ORM 实现几乎精确的 SQL 查询。

from django.db.models import Func, Field, Lookup    

class Tuple(Func):
    function = ''
    template = '(%(expressions)s)'

@Field.register_lookup
class NotIn(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

qs = (MyTable.objects.annotate(tup_fields=Tuple('field1', 'field2', output_field=Field()))
             .filter(tup_fields__not_in=data))

这将产生类似 SQL

SELECT field1, field2, field3, (field1, field2) as tup_fields
FROM mytable
WHERE ((mytable.field1, mytable.field2) NOT IN ((1, 1), (2, 2)));

您可以不用NotIn(Lookup) 类而使用.filter(~Q(tup_fields__in=data))。这将生成一个类似 NOT (field1, field2) IN (...) 的 where 子句。

您也可以按照自己的方式进行操作,这可能会很好。生成的 SQL 不同,但结果是一样的。我不确定是否存在对您很重要的性能差异。

【讨论】:

    【解决方案2】:

    在这里你可以这样做

    from django.db.models import Q
    
    query = Q()
    
    for (field1, field2) in data:  # List[Tuple[int, int]]
        query.add(Q(field1=field1, field2=field2), Q.OR)
    
    qs = MyTable.objects.exclude(query)
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-04
      • 1970-01-01
      • 2020-10-23
      • 2021-09-13
      • 2020-09-14
      • 1970-01-01
      • 1970-01-01
      • 2015-12-30
      相关资源
      最近更新 更多