Django ManyToMany filtresi ()


131

Bir modelim var:

class Zone(models.Model):
    name = models.CharField(max_length=128)
    users = models.ManyToManyField(User, related_name='zones', null=True, blank=True)

Ve şu satırlar boyunca bir filtre yapmam gerekiyor:

u = User.objects.filter(...zones contains a particular zone...)

Kullanıcı üzerinde bir filtre olmalı ve tek bir filtre parametresi olmalıdır. Bunun nedeni, yönetici kullanıcı değişiklik listesini filtrelemek için bir URL sorgu dizesi oluşturmamdır:http://myserver/admin/auth/user/?zones=3

Görünüşe göre basit olmalı ama beynim işbirliği yapmıyor!


8
Seni doğru anladığımdan emin değilim - bunun için değil User.objects.filter(zones__id=<id>)mi, User.objects.filter(zones__in=<id(s)>)iyi mi?
Tomasz Zieliński

Sorun değil :) BTW User.objects.filter(zones__in=<id(s)>)muhtemelen şöyle olmalıUser.objects.filter(zones__id__in=<id(s)>)
Tomasz Zieliński

22
Bunu Google'da araştıran herkese, yalnızca related_name ayarlandığında işe yarayacağını belirtmek istedim. zone_set, örneğin çalışmaz. Bunun için iyi bir yarım saat

Yanıtlar:


156

Sadece Tomasz'ın söylediklerini tekrar ediyorum.

Birçok örnek vardır FOO__in=...stil filtreler için çok-çok ve çok-one testler. İşte özel probleminiz için sözdizimi:

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

Çift alt çizgi (__) sözdizimi, sorgu kümeleriyle çalışırken her yerde kullanılır .


Teşekkürler @maxm. Bazı örneklere daha güncel bir bağlantıyla güncellendi.
istruble

9
çift alt çizgi (argh o birine kayıp 3 saat.)
reabow

Sadece herhangi biri değil, bir dizi bölgede bulunan kullanıcıları istiyorsam ne yapmalıyım? Diyelim ki zone1, zone3, .. ve zone 10
FRR

Daha ...__insonra örneklere bakın # filtering on a few zones, by id. Bunlar birden çok kimlik / nesne için filtrelemeyi gösterir (bu durumda). Önem verdiğiniz zone1, zone3 ve zone10 kimliklerini / nesnelerini geçmeniz yeterli. Veya gerekirse dördüncü ekleyin.
istruble

Teşekkürler. Tek değeri içeren bir dizi yerine yalnızca tek bir değere göre filtreleme yapıyordum.
zypro

36

Kullanıcı sorguda kullanılan birden çok bölgede bulunuyorsa, muhtemelen .distinct () eklemek isteyebileceğinizi unutmayın. Aksi takdirde, bir kullanıcıya birden çok kez sahip olursunuz:

users_in_zones = User.objects.filter(zones__in=[zone1, zone2, zone3]).distinct()

1

Bunu yapmanın başka bir yolu da ara tablodan geçmektir. Bunu Django ORM içinde şu şekilde ifade ediyorum:

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

.values('user')Belirtilene ihtiyacı olmasa güzel olurdu , ancak Django'nun (sürüm 3.0.7) buna ihtiyacı var gibi görünüyor.

Yukarıdaki kod şuna benzer bir SQL oluşturacaktır:

SELECT * FROM users WHERE id IN (SELECT user_id FROM userzones WHERE zone_id IN (1,2,3))

bu güzel çünkü yinelenen kullanıcıların döndürülmesine neden olabilecek ara birleştirme yok


Selam. Bu kendi başına bir cevap değil. Ek bir kısmi cevap eklemek yerine bir yorum eklemeli veya QB'nin cevabını düzenlemelisiniz.
Andy Baker

Evet - cevabınızı kendi içinde eksiksiz olacak şekilde düzenlemek istiyorsanız (QB'nin cevabını düzenlemek için yeterli karmanız yoksa?), O zaman bu en iyi bahis olacaktır. İdeal olarak StackOverflow'da "bir doğru cevap" vardır. Genelde o kadar düzgün çalışmaz ama hedeflemeye değer.
Andy Baker

@AndyBaker kabul etti! Geriye dönüp bakıldığında, QB'nin cevabı muhtemelen istruble'nin cevabı üzerine bir yorum olmalıdır, oysa benimkinin ayrı bir cevabı gerektirecek kadar farklı olduğunu düşünüyorum, ama evet
Sam Mason
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.