Фільтр Django 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)>)добре для цього?
Томаш Зеліньський

Це добре :) BTW, User.objects.filter(zones__in=<id(s)>)мабуть, має бутиUser.objects.filter(zones__id__in=<id(s)>)
Tomasz Zieliński,

21
Я просто хотів вказати на когось із Google, щоб це працювало лише за умови встановлення related_name. Наприклад, зона_сети не працюватиме. Витратили на це добрі півгодини :-)

Відповіді:


155

Просто переказуючи те, що сказав Томаш.

Є багато прикладів 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])

Синтаксис подвійного підкреслення (__) використовується в усьому місці під час роботи з наборами запитів .


Дякую @maxm. Оновлено більш поточним посиланням на деякі приклади.
istruble

9
подвійне підкреслення (близько 3 годин, втрачене на цю)
реабілітувати

Скажіть, будь ласка, що робити, якщо я хочу, щоб користувачі, які перебувають у наборі зон, а не будь-яка з них? Скажімо, знайдіть користувачів, які перебувають у zone1, zone3, .. та зоні 10
FRR

Подивіться ...__inприклади після # filtering on a few zones, by id. Вони показують фільтрацію для декількох ідентифікаторів / об’єктів (у цьому випадку). Просто перейдіть в ідентифікатори zone1, zone3 та zone10 / об'єкти, які вас цікавлять. Або додайте 4-ту, якщо потрібно.
іструбл

Дякую. Я фільтрував лише одне значення, а не масив, що містить єдине значення.
зипро

36

Зауважте, що якщо користувач може знаходитись у кількох зонах, які використовуються в запиті, можливо, ви захочете додати .distinct (). В іншому випадку ви отримуєте одного користувача кілька разів:

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

1

Ще один спосіб зробити це, пройшовши проміжну таблицю. Я б висловив це в рамках Django 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') вказаного, але Django (версія 3.0.7), схоже, потрібен.

вищезазначений код в результаті генерує SQL, який виглядає приблизно так:

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

що приємно, оскільки не має проміжних з'єднань, які могли б викликати повернення дублікатів користувачів


Hiya. Це сама по собі не відповідь. Вам слід додати коментар або відредагувати відповідь QB, а не додавати додаткову часткову відповідь.
Енді Бейкер

Так - якщо ви хочете відредагувати свою відповідь, щоб вона була повною, вона є власною (якщо у вас недостатньо карми для редагування відповіді QB?), Тоді це було б найкращим варіантом. В ідеалі на StackOverflow є "одна правильна відповідь". Зазвичай це виходить не дуже акуратно, але варто орієнтуватися.
Енді Бейкер

@AndyBaker погодився! в ретроспективі відповідь QB, мабуть, має бути коментарем до відповіді istruble, хоча я думаю, що мій досить чіткий, щоб вимагати окремої відповіді, але ну добре
Сем Мейсон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.