我在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上没有。
答案 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)。如果它对您至关重要,请使用更复杂的东西作为分隔符(例如':')。