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() не отфильтровывает комбинации с одинаковыми записями, но с разным порядком.

Вернуться на верх