Mam modelkę:
class Zone(models.Model):
name = models.CharField(max_length=128)
users = models.ManyToManyField(User, related_name='zones', null=True, blank=True)
I muszę zbudować filtr w następujący sposób:
u = User.objects.filter(...zones contains a particular zone...)
Musi to być filtr użytkownika i musi to być pojedynczy parametr filtru. Powodem tego jest to, że konstruuję zapytanie dotyczące adresu URL, aby filtrować listę zmian użytkownika administratora:http://myserver/admin/auth/user/?zones=3
Wydaje się, że to powinno być proste, ale mój mózg nie współpracuje!
django
django-models
Andy Baker
źródło
źródło
User.objects.filter(zones__id=<id>)
aniUser.objects.filter(zones__in=<id(s)>)
dobry do tego?User.objects.filter(zones__in=<id(s)>)
prawdopodobnie powinno byćUser.objects.filter(zones__id__in=<id(s)>)
Odpowiedzi:
Powtarzam tylko to, co powiedział Tomasz.
Istnieje wiele przykładów
FOO__in=...
filtrów stylów w testach wiele do wielu i wiele do jednego . Oto składnia dla twojego konkretnego problemu:users_in_1zone = User.objects.filter(zones__id=<id1>) # same thing but using in users_in_1zone = User.objects.filter(zones__in=[<id1>]) # filtering on a few zones, by id users_in_zones = User.objects.filter(zones__in=[<id1>, <id2>, <id3>]) # and by zone object (object gets converted to pk under the covers) users_in_zones = User.objects.filter(zones__in=[zone1, zone2, zone3])
Składnia podwójnego podkreślenia (__) jest używana w każdym miejscu podczas pracy z zestawami zapytań .
źródło
...__in
poniższe przykłady# filtering on a few zones, by id
. Te pokazują filtrowanie dla wielu identyfikatorów / obiektów (w tym przypadku). Po prostu podaj identyfikatory / obiekty zone1, zone3 i zone10, na których Ci zależy. Lub dodaj 4. w razie potrzeby.Zauważ, że jeśli użytkownik może znajdować się w wielu strefach używanych w zapytaniu, prawdopodobnie będziesz chciał dodać
.distinct()
. W przeciwnym razie jednego użytkownika otrzymujesz wielokrotnie:users_in_zones = User.objects.filter(zones__in=[zone1, zone2, zone3]).distinct()
źródło
innym sposobem jest przejście przez tabelę pośrednią. Wyraziłbym to w Django ORM w ten sposób:
UserZone = User.zones.through # for a single zone users_in_zone = User.objects.filter( id__in=UserZone.objects.filter(zone=zone1).values('user')) # for multiple zones users_in_zones = User.objects.filter( id__in=UserZone.objects.filter(zone__in=[zone1, zone2, zone3]).values('user'))
byłoby miło, gdyby nie wymagał
.values('user')
określonego, ale wydaje się, że Django (wersja 3.0.7) tego potrzebuje.powyższy kod zakończy generowanie kodu SQL, który wygląda mniej więcej tak:
SELECT * FROM users WHERE id IN (SELECT user_id FROM userzones WHERE zone_id IN (1,2,3))
co jest miłe, ponieważ nie ma żadnych pośrednich złączeń, które mogłyby spowodować zwrócenie zduplikowanych użytkowników
źródło