Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Django中创建基于单个条件过滤多个字段的查询集?_Django_Django Models - Fatal编程技术网

如何在Django中创建基于单个条件过滤多个字段的查询集?

如何在Django中创建基于单个条件过滤多个字段的查询集?,django,django-models,Django,Django Models,我的模型是 class TestModel(models.Model) field1 = models.IntegerField() field2 = models.IntegerField() field3 = models.IntegerField() field4 = models.IntegerField() field5 = models.IntegerField() 我需要一个简单的查询集,它在模型的所有五个字段上应用一个条件,而无需编写字

我的模型是

class TestModel(models.Model) 
    field1 = models.IntegerField()
    field2 = models.IntegerField()
    field3 = models.IntegerField()
    field4 = models.IntegerField()
    field5 = models.IntegerField()
我需要一个简单的查询集,它在模型的所有五个字段上应用一个条件,而无需编写字段的每个组合并过滤它们

例如,我想对两个或多个字段应用检查无的条件

TestModel.objects.filter(two_or_more_fields=None)
我不想编写5个字段的所有可能组合来查找查询集,其中任何两个或多个字段都是None。换句话说,有没有比以下更好的方法来实现这一点:

from django.db.models import Q
TestModel.objects.filter(
    #condition for exactly 2 None
    Q(field1=None & field2=None) |
    Q(field2=None & field3=None) |
    Q(field3=None & field4=None) |
    Q(field4=None & field5=None) |
    Q(field5=None & field1=None) |
    #condition for more than 2 None
    Q(field1=None & field2=None & field3 = None) |
    '''''
    .
    .
    #so on to cover all possible cases of any two or more fields as None
     )

我认为应该有一个更好、更简单的方法来实现这一点

我不知道Django中有任何方法可以根据非空字段的数量进行过滤

我建议的一个不那么笨拙和高效的解决方法是在模型本身中将空字段的数量(
null\u count
)存储为一个字段

您可以通过轻松重写
TestModel
save
方法来实现这一点

def save(self, *args, **kwargs):
    self.null_count = 0
    self.null_count += 1 if self.field1 is None else 0
    # update null_count for 4 remaining fields
    super(TestModel, self).save(*args, **kwargs)
在视图中,过滤
null\u count

TestModel.objects.filter(null_count_gt=2)

在花了几个小时之后,我找不到一个简单的方法来使用内置的Django过滤器结构来实现这一点。然而,我发现这个解决方案更接近我所寻找的:

field_list = ['field1', 'field2', 'field3', 'field4', 'field5']

def get_all_possible_filter_dict_list_for_a_condition(field_list):

    all_possible_filter_dict_for_a_condition = []
    for field_1, field_2 in combinations(field_list, 2):
        all_possible_filter_dict_for_a_condition.append(
        {
         field_1:None,
         field_2:None 
         }
        )
    return all_possible_filter_dict_for_a_condition


def get_qs_list_to_perform_or_operation(all_possible_filter_dict_list_for_a_condition):

    qs_list_to_perform_or_operation = []
    for i, filter_dict in enumerate(all_possible_filter_dict_list_for_a_condition):
       qs_to_append = qs.filter(**filter_dict)
       qs_list_to_perform_or_operation.append(qs_to_append)
    return qs_list_to_perform_or_operation


def get_qs_to_filter_fields_with_more_than_1_none(qs_list_to_perform_or_operation ):

    final_qs = qs_list_to_perform_or_operation [0]
    for i in range(len(qs_list_to_perform_or_operation ) - 1):
        final_qs = final_qs | qs_list[i + 1]
    return final_qs

all_possible_filter_dict_list_for_a_condition 
   = get_all_possible_filter_dict_list_for_a_condition(field_list)
qs_list_to_perform_or_operation = get_qs_list_to_perform_or_operation(all_possible_filter_dict_list_for_a_condition)
final_qs_to_filter_multiple_fields_with_same_condtion = get_qs_to_filter_fields_with_more_than_1_none(qs_list_to_perform_or_operation)

由于此处使用的函数,此解决方案是特定于POSTGRESQL的。但是,如果您正在使用另一个数据库,则可以使用此想法。


您可以混合使用
RawSQL
和一些
ARRAY
magic

其思想是使用不为空的字段数对queryset中的每个项进行注释:

from django.db.models import Func
from django.db.models.expressions import RawSQL

field_list = ['field1', 'field2', 'field3', 'field4', 'field5']

# field1 IS NULL, field2 IS NULL, field3 IS NULL, .....
statements = ", ".join("{} IS NULL".format(field) for field in field_list)

# create an array for each field by checking if those are NULL
# then, remove the False values e.g. not null ones.
# ARRAY_REMOVE(ARRAY[field1 IS NULL, field2 IS NULL, .....], false)
raw_sql = "ARRAY_REMOVE(ARRAY[{statements}], false)".format(statements=statements)

# now annotate each item with the number of null fields
qs = TestModel.objects.annotate(num_null=Func(RawSQL(sql, ()), function='CARDINALITY')

# on this queryset you can filter by the number of null items you want to check
qs = qs.filter(num_null__gte=2)

这是一个很好的建议,但我不能改变我的情况下的模式。