장고 ManyToMany 필터 ()


131

모델이 있습니다.

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

그리고 나는 다음과 같은 라인을 따라 필터를 구성해야합니다.

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

사용자의 필터 여야하며 단일 필터 매개 변수 여야합니다. 그 이유는 관리자 사용자 변경 목록을 필터링하기 위해 URL 쿼리 문자열을 구성하고 있기 때문입니다.http://myserver/admin/auth/user/?zones=3

그것은 단순해야하지만 내 두뇌가 협조하지 않는 것 같습니다!


8
아니다 - 내가 당신을 얻을 수 있을지 잘 모르겠어요 User.objects.filter(zones__id=<id>)또는 User.objects.filter(zones__in=<id(s)>)이 좋은?
Tomasz Zieliński

괜찮습니다 :) BTW User.objects.filter(zones__in=<id(s)>)는 아마해야합니다User.objects.filter(zones__id__in=<id(s)>)
Tomasz Zieliński

21
related_name이 설정된 경우에만 작동한다는 것을 인터넷 검색하는 사람에게 지적하고 싶었습니다. 예를 들어 zone_set이 작동하지 않습니다. 그 :-)에 좋은 반 시간 낭비

답변:


155

Tomasz가 말한 것을 쉬고 있습니다.

대다 및 다 대일 테스트 에는 FOO__in=...스타일 필터의 예가 많이 있습니다 . 특정 문제에 대한 구문은 다음과 같습니다.

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

이중 밑줄 (__) 구문은 querysets 작업시 모든 곳에서 사용됩니다 .


감사합니다 @maxm. 일부 예제에 대한 최신 링크로 업데이트되었습니다.
istruble

9
이중 밑줄 (3 시간 씩
줄임

하나의 영역이 아닌 여러 영역에있는 사용자를 원한다면 어떻게해야합니까? zone1, zone3, .. 및 zone 10에있는 사용자를 찾으십시오
FRR

...__in이후 의 예를보십시오 # filtering on a few zones, by id. 여러 ID / 객체에 대한 필터링을 보여줍니다 (이 경우). 원하는 zone1, zone3 및 zone10 ID / 개체 만 전달하면됩니다. 또는 필요한 경우 4를 추가하십시오.
istruble

고마워. 단일 값을 포함하는 배열 대신 단일 값만 필터링했습니다.
zypro

36

사용자가 쿼리에 사용 된 여러 영역에있는 경우 .distinct ()를 추가 할 수 있습니다. 그렇지 않으면 한 명의 사용자가 여러 번 나타납니다.

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

1

이를 수행하는 또 다른 방법은 중간 테이블을 거치는 것입니다. 나는 이것을 장고 ORM 내에서 이렇게 표현했다 :

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')지정이 필요하지 않으면 좋을 것입니다 . 그러나 장고 (버전 3.0.7)에는 필요합니다.

위의 코드는 다음과 같은 SQL을 생성합니다.

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

중복 사용자가 반환 될 수있는 중간 조인이 없기 때문에 좋습니다.


히야 이것은 그 자체로 답이 아닙니다. 추가 부분 답변을 추가하는 대신 의견을 추가하거나 QB의 답변을 편집해야합니다.
Andy Baker

네-답변을 편집하고 싶다면 완전하게 완성됩니다 (QB의 답변을 편집 할 충분한 업장이 없다면?). 이것이 최선의 방법입니다. 이상적으로 StackOverflow에는 "하나의 정답"이 있습니다. 일반적으로 깔끔하게 해결되지는 않지만 목표로 할 가치가 있습니다.
Andy Baker

@AndyBaker는 동의했다! 돌이켜 보면 QB의 답변은 아마도 istruble의 답변에 대한 의견 일 것입니다. 반면에 제 답변은 별도의 답변을 보증하기에 충분하지만 아 잘됩니다
Sam Mason
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.