Bộ lọc Django ManyToMany ()


131

Tôi có một mô hình:

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

Và tôi cần phải lọc một bộ lọc dọc theo dòng:

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

Nó phải là một bộ lọc trên Người dùng và nó phải là một tham số bộ lọc duy nhất. Lý do cho điều này là vì tôi đang xây dựng một chuỗi truy vấn URL để lọc thay đổi người dùng quản trị viên:http://myserver/admin/auth/user/?zones=3

Có vẻ như nó đơn giản nhưng bộ não của tôi không hợp tác!


8
Tôi không chắc chắn nếu tôi hiểu đúng về bạn - không tốt User.objects.filter(zones__id=<id>)hay User.objects.filter(zones__in=<id(s)>)tốt cho việc này?
Tomasz Zieliński

Điều đó ổn :) BTW User.objects.filter(zones__in=<id(s)>)có lẽ nên làUser.objects.filter(zones__id__in=<id(s)>)
Tomasz Zieliński

21
Chỉ muốn chỉ ra cho bất kỳ ai Googling điều này, rằng nó chỉ hoạt động nếu có tên_bảng được đặt. Zone_set sẽ không hoạt động, ví dụ. Đã lãng phí nửa giờ tốt về điều đó :-)

Câu trả lời:


155

Chỉ cần nghỉ ngơi những gì Tomasz nói.

Có nhiều ví dụ về các FOO__in=...bộ lọc kiểu trong các thử nghiệm nhiều-nhiềunhiều-một . Đây là cú pháp cho vấn đề cụ thể của bạn:

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

Gạch dưới đôi (__) cú pháp được sử dụng khắp nơi khi làm việc với queryset .


Cảm ơn @maxm. Cập nhật với một liên kết hiện tại với một số ví dụ.
istruble

9
gạch dưới gấp đôi (mất 3 giờ cho cái đó)
tái lập

Bạn có thể vui lòng nói, phải làm gì nếu tôi muốn người dùng ở trong một khu vực không chỉ là một trong số họ không? Hãy nói rằng tìm người dùng ở khu vực 1, khu
FRR

Nhìn vào các ...__inví dụ sau # filtering on a few zones, by id. Những người hiển thị lọc cho nhiều id / đối tượng (trong trường hợp này). Chỉ cần vượt qua trong id / id khu vực1, khu vực 3 và khu vực 10 mà bạn quan tâm. Hoặc thêm lần thứ 4 nếu cần.
istruble

Cám ơn. Tôi chỉ lọc theo một giá trị duy nhất, thay vì một mảng chứa giá trị đơn.
zypro

36

Lưu ý rằng nếu người dùng có thể ở nhiều vùng được sử dụng trong truy vấn, bạn có thể muốn thêm .distotype (). Nếu không, bạn nhận được một người dùng nhiều lần:

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

1

một cách khác để làm điều này là bằng cách đi qua bảng trung gian. Tôi muốn thể hiện điều này trong Django ORM như thế này:

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

Sẽ thật tuyệt nếu nó không cần .values('user')chỉ định, nhưng Django (phiên bản 3.0.7) dường như cần nó.

đoạn mã trên sẽ kết thúc việc tạo SQL trông giống như:

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

thật tuyệt vì nó không có bất kỳ phép nối trung gian nào có thể khiến người dùng trùng lặp bị trả về


Hiya. Đây không phải là một câu trả lời. Bạn nên thêm một nhận xét hoặc chỉnh sửa câu trả lời của QB thay vì thêm một câu trả lời một phần.
Andy Baker

Vâng - nếu bạn muốn chỉnh sửa câu trả lời của mình sao cho nó hoàn chỉnh theo đúng nghĩa của nó (trừ khi bạn có đủ nghiệp lực để chỉnh sửa câu trả lời của QB?) Thì đó sẽ là cách tốt nhất. Lý tưởng nhất trên StackOverflow có "một câu trả lời đúng". Nó thường không hoạt động khá gọn gàng nhưng nó đáng để hướng tới.
Andy Baker

@AndyBaker đồng ý! khi nhìn lại câu trả lời của QB có lẽ nên là một nhận xét về câu trả lời của istruble, trong khi tôi nghĩ câu trả lời của tôi đủ khác biệt để đảm bảo câu trả lời riêng biệt, nhưng ah tốt
Sam Mason
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.