Как аннотировать набор запросов 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_nameof aForeignKeyorManyToManyFieldis plural, for examplecomponentsinstead of.component