Powiedzmy, że mam następujące modele
class Photo(models.Model):
tags = models.ManyToManyField(Tag)
class Tag(models.Model):
name = models.CharField(max_length=50)
W widoku mam listę z aktywnymi filtrami zwanymi kategoriami . Chcę filtrować obiekty fotograficzne, które mają wszystkie tagi obecne w kategoriach .
Próbowałem:
Photo.objects.filter(tags__name__in=categories)
Ale to pasuje do dowolnego elementu w kategoriach, nie do wszystkich elementów.
Jeśli więc kategoriami byłyby [„wakacje”, „lato”], chcę mieć zdjęcia z tagami zarówno wakacji, jak i wakacji.
Czy można to osiągnąć?
python
django
filter
django-queryset
Sander van Leeuwen
źródło
źródło
Photo.objects.filter(tags__name='holiday').filter(tags__name='summer')
to droga do zrobienia. (To jest to samo, co przykład jpic). Każdyfilter
powinien dodać więcejJOIN
s do zapytania, abyś mógł zastosować adnotacje, jeśli jest ich zbyt wiele.Odpowiedzi:
Podsumowanie:
Jedną z opcji jest, zgodnie z sugestiami jpic i sgallen w komentarzach, dodanie
.filter()
dla każdej kategorii. Każdy kolejnyfilter
dodaje więcej sprzężeń, co nie powinno stanowić problemu dla małego zestawu kategorii.Istnieje podejście agregacyjne . To zapytanie byłoby krótsze i być może szybsze w przypadku dużego zestawu kategorii.
Masz również możliwość korzystania z zapytań niestandardowych .
Kilka przykładów
Konfiguracja testowa:
Korzystanie z metody filtrów łańcuchowych :
Wynikowe zapytanie:
Zauważ, że każdy
filter
dodaje więcejJOINS
do zapytania.Korzystanie z adnotacji podejścia :
Wynikowe zapytanie:
AND
edQ
obiekty nie będą działać:Wynikowe zapytanie:
źródło
t3
, a zdjęcie ma tagit2
it3
. Wtedy to zdjęcie będzie nadal pasować do zadanego zapytania.Photo.objects.filter(tags__in=tags)
dopasowuje zdjęcia, które mają dowolny z tagów, nie tylko te, które mają wszystkie. Niektóre z tych, które mają tylko jeden z pożądanych tagów, mogą mieć dokładnie taką liczbę tagów, których szukasz, a niektóre z tych, które mają wszystkie pożądane tagi, mogą również mieć dodatkowe tagi.Innym podejściem, które działa, chociaż tylko PostgreSQL, jest użycie
django.contrib.postgres.fields.ArrayField
:Przykład skopiowany z dokumentów :
ArrayField
ma kilka bardziej zaawansowanych funkcji, takich jak przekształcenia nakładania i indeksowania .źródło
Można to również zrobić poprzez dynamiczne generowanie zapytań przy użyciu Django ORM i trochę magii Pythona :)
Chodzi o to, aby wygenerować odpowiednie obiekty Q dla każdej kategorii, a następnie połączyć je za pomocą operatora AND w jeden zestaw QuerySet. Np. Dla twojego przykładu byłoby to równe
źródło
filter
będzie tym samym, co używanieand
dla obiektów Q w jednym filtrze ... Mój błąd.filter
naexclude
i użyjesz operatora ujemnego. Na przykład:res = Photo.exclude(~reduce(and_, [Q(tags__name=c) for c in categories]))
Używam małej funkcji, która iteruje filtry po liście dla danego operatora i nazwy kolumny:
a tę funkcję można nazwać w ten sposób:
działa również z każdą klasą i innymi tagami na liście; operatory mogą być dowolnymi operatorami, takimi jak „iexact”, „in”, „zawiera”, „ne”, ...
źródło
źródło
Jeśli chcemy to robić dynamicznie, postępowaliśmy zgodnie z przykładem:
źródło