2017-03-29 48 views
5

Mój model jestJak utworzyć zestaw zapytań, który filtruje wiele pól w oparciu o jeden warunek w Django?

class TestModel(models.Model) 
    field1 = models.IntegerField() 
    field2 = models.IntegerField() 
    field3 = models.IntegerField() 
    field4 = models.IntegerField() 
    field5 = models.IntegerField() 

Potrzebuję prostego queryset że zastosowanie jednego warunku na wszystkich pięciu dziedzinach modelu bez pisania każdą kombinację pól i ich filtrowanie.

Na przykład chcę, aby zastosować warunek sprawdzania Żaden z dwóch lub więcej pól

TestModel.objects.filter(two_or_more_fields=None) 

Nie chcę napisać każdą możliwą kombinację 5 pól znaleźć queryset z dwiema lub więcej pól jako Brak. Innymi słowy, czy istnieje lepszy sposób osiągnięcia tego celu niż:

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 
    ) 

myślę, że powinno być lepiej i prosty sposób do osiągnięcia tego celu.

Odpowiedz

3

Po spędzeniu godzin nie mogłem znaleźć prostego sposobu, aby to zrobić, korzystając z wbudowanych konstrukcji filtrów Django. Jednak uważam, że to rozwiązanie, które jest bliżej do czego szukałem:

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) 
1

Nie jestem świadomy żadnej metody filtrowania według liczby niezerowych pól w Django.

Jednym z mniej kłopotliwych i skutecznych metod wyszukiwania sugerowałbym przechowywanie liczby pól zerowych (null_count) w samym modelu jako polu.

Możesz to zrobić, łatwo zastępując metodę save pod numerem TestModel.

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) 

iw świetle, filtr na null_count.

TestModel.objects.filter(null_count_gt=2) 
+0

Jest to dobra propozycja, ale nie mogę zmienić model w moim przypadku. – javed

0

To rozwiązanie jest PostgreSQL specyficzne ze względu na funkcji używanych tutaj. Pomysł można jednak wykorzystać, jeśli używasz innej bazy danych.


Można stosować mieszaninę RawSQL i trochę ARRAY magii.

Chodzi o to, aby każdy element adnotacji w queryset z liczbą pól, które nie są puste:

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) 
+0

Czy to działa dla Ciebie? – AKS