Django: Получение списка уникальных комбинаций m2m
У меня есть следующая схема модели в Django (Обратите внимание, что я опустил подробный обзор класса ModelB, поскольку он не имеет отношения к вопросу)
class ModelA(Models.model):
b = models.ManyToManyField(ModelB, blank=True)
Данный набор запросов из объектов ModelA, я хотел бы получить все уникальные M2M комбинации (объектов ModelB), которые присутствуют в наборе запросов. Например, для следующего набора запросов из 4 объектов ModelA:
| id | b (m2m field) |
| -------- | ---------------- |
| 1 | [1,2,3] |
| 2 | [1,2] |
| 3 | [1,2] |
| 4 | [] |
Я хотел бы возвращать список/кэрисет, который выглядит как [[1,2,3],[1,2],[]]. Есть ли элегантный способ сделать это в Django? Я пытался сделать следующее: `
queryset.values_list('b',flat=True).distinct()
Но это возвращает плоский список всех уникальных объектов ModelB, присутствующих во всей таблице (например: [1,2,3]). Следовательно, я теряю детализацию комбинаций, в которых объекты ModelB встречаются в поле m2m.
Вы можете получить доступ к сквозной модели через ModelA.b.through. Оттуда вы можете сделать запрос к этой модели, которая будет иметь поля как ModelB, так и ModelA. Структура модели будет выглядеть примерно так:
ThroughModel:
id: int
a_id: int
a: ModelA
b_id: int
b: ModelB
После этого вам все равно нужно будет разработать запрос, чтобы получить данные в нужной вам форме. Вы должны быть в состоянии достичь этого с помощью Subquery и ArrayAgg
Для будущих читателей с тем же вопросом, я закончил использованием комбинации annotate и ArrayAgg следующим образом:
unique_combinations = (
ModelA.objects.annotate(id_list=ArrayAgg("b", ordering="b", distinct=True))
.values("id_list")
.distinct()
)
Сначала я аннотирую каждый объект полем id_list, которое содержит все отсортированные и уникальные идентификаторы ModelB объектов в отношении m2m. Затем я извлекаю значения id_list из кверисета и сохраняю только отдельные элементы. В результате получается кверисет, содержащий все (уникальные) m2m-комбинации ModelB объектов, присутствующих в ModelA кверисете.
Обратите внимание, что при применении ArrayAgg во время аннотирования важно добавить порядок, так как оператор .distinct() не отфильтровывает комбинации с одинаковыми записями, но с разным порядком.