Как аннотировать набор запросов Django с подсчетом связанных объектов
У меня есть 3 модели django; Machine
, Component
и Part
.
Я пытаюсь аннотировать набор запросов машины, чтобы включить в него количество уникальных деталей, используемых в этой машине.
В приведенном ниже примере ps1 используется для создания f1
. ps1
и ps2
используются для создания f2
. f1
и f2
используются для создания r1
. Таким образом, для создания r1
используются 2 уникальные части, и именно это значение я хочу использовать для аннотации r1
. Пока что мой код выводит 3 в этом случае.
# |------------+---------------------+----------------+--------------+--------------|
# | machine | component | part | expected | result |
# | | | | parts | parts |
# |------------+---------------------+----------------+--------------+--------------|
# | r1 | f1 | ps1 | 2 | 3 |
# | | f2 | ps1 | | |
# | | | ps2 | | |
# |------------+---------------------+----------------+--------------+--------------|
Part
имеетManyToManyField
отComponent
.Component
имеетForeignKey
кMachine
.
Вот функция, с которой я работаю, чтобы попытаться достичь этого.
def annotate_machine_query_with_num_parts(machine_query):
count_subquery = (
Parts.objects.filter(pk=OuterRef("pk"))
.annotate(count=Count("id", distinct=True))
.values("count")
)
sum_subquery = (
Parts.objects.filter(
Q(component__machine_id=OuterRef("pk"))
)
.annotate(count=Subquery(count_subquery))
.values("count")
.annotate(num_parts=Sum("count"))
.values("num_parts")
)
return machine_query.annotate(num_parts=Coalesce(sum_subquery, 0))
Это работает во многих тестовых случаях, но не работает, когда деталь повторно используется несколькими компонентами, как в случае с ps1
здесь.
Я попробовал несколько способов, которые привели к ошибкам subquery must return a single row
. Я уверен, что должен быть более простой способ добиться этого.
I think you are overcomplicating things. You can work with distinct=True
[Django-doc]:
from django.db.models import Count
Machine.annotate(num_parts=Count('component__parts', distinct=True))
Note: The
related_name=…
[Django-doc] is the name of the manager to fetch the related objects in reverse. Therefore normally therelated_name
of aForeignKey
orManyToManyField
is plural, for examplecomponents
instead of.component