Django:过滤以检查两个/多个字段是否在列表中

时间:2017-09-18 11:26:49

标签: python django django-queryset

我在Django中有一个模型ModelNameHere,字段为field_a和field_b。 我在python中有一个列表,其中包含形式为(a,b)的元组

我想得到Django模型的所有实例,其中两个字段都在Python列表中。

如果只有一个字段,这很简单:

my_filter_list = [a1, a2, a3]
ModelNameHere.objects.filter(field_a__in=my_filter_list)

但对于多个字段,这不起作用

my_filter_list = [(a1, b1), (a2, b2), (a3, b3)]
ModelNameHere.objects.filter( ? )

有没有办法同时检查这两个字段?

或者,有没有办法将my_filter_list转换为临时表,以便我可以使用field_a和field_b作为连接键将该表与ModelNameHere连接?

编辑:

快速添加:我在field_a上设置了索引,但在field_b上没有。

4 个答案:

答案 0 :(得分:2)

您可以对查询kwargs使用字典理解,并对查询集使用管道运算符进行OR运算。

query_fields = ({ 'field_a': value_a, 'field_b': value_b } for value_a, value_b in my_filter_list)

queryset = ModelNameHere.objects.none()
for query_kwarg in query_fields:
    queryset |= ModelNameHere.objects.filter(**query_kwarg)
queryset = queryset.distinct()

答案 1 :(得分:1)

如果您的my_filter_list变量预计不会太长(因为您可能遇到性能问题),您可以构建Q查询:

from django.db.models import Q

multi_field_filter = None
for field_a_val, field_b_val in my_filter_list:
    # logical AND the two field values together
    q = Q(field_a=field_a_val, field_b=field_b_val)
    # logical OR the AND'd terms together
    multi_field_filter = q if multi_field_filter is None else (multi_field_filter | q)

results = ModelNameHere.objects.all()
if multi_field_filter is not None:
    results = ModelNameHere.objects.filter(multi_field_filter)

对于更长的列表,人们不希望生成大量的SQL来执行这些类型的查询。一个(可能是hacky)解决方案还可能涉及在模型中添加一个额外的字段,以可搜索的方式连接这两个值,如下所示:

class ModelNameHere(models.Model):
    field_a = models.IntegerField()
    field_b = models.IntegerField()
    fields_concat = models.CharField(max_length=200, blank=True, db_index=True)

    def save(self, *args, **kwargs):
        # automatically update on save
        self.fields_concat = "%s.%s" % (self.field_a, self.field_b)
        # call parent save
        super(ModelNameHere, self).save(*args, **kwargs)

然后,当您想要查找特定组合时,只需进行精确的文本搜索匹配:

# using set() automatically deduplicates any search combinations
concat_filters = set([
    ("%s.%s" % (field_a_val, field_b_val)) for field_a_val, field_b_val in my_filter_list
])
# do your lookup here
results = ModelNameHere.objects.filter(fields_concat__in=concat_filters)

答案 2 :(得分:1)

你可以使用枚举,像这样使用Q的组合:

from django.db.models import Q
result_list = []
my_filter_list = [(a1, b1), (a2, b2), (a3, b3)]

for n,m in enumerate(my_filter_list):
    query_set = ModelNameHere.objects.filter(Q(field_a=n[0]) | Q(field_b=m[0]))
    if query_set.count() >0:
        result_list.append(query_set)

答案 3 :(得分:0)

Annotate a field which combines both values

from django.db.models.functions import Concat
from django.db.models import Value

my_filter_list = [(a1, b1), (a2, b2), (a3, b3)]
separator = ' '
ModelNameHere.objects.annotate(
    a_b=Concat('a', Value(separator), 'b')
).filter(
    a_b__in=['{}{}{}'.format(a, separator, b) for a, b in my_filter_list]
)

好处是它是一个单一的查询,并且易于理解。

不好的是,你不仅会获得(a1,b1)和(a2,b2),还会获得(a1 b1,)和(,a1 b1)。如果它对您至关重要,请使用更复​​杂的东西作为分隔符(例如':')。