Model code:
# part.py
class Part(models.Model):
id = HashidSaltedAutoField(primary_key=True)
is_active = models.BooleanField(default=True)
components = models.ManyToManyField("Component", related_name="parts")
# component.py
class Component(models.Model):
id = HashidSaltedAutoField(primary_key=True)
engine_machine = models.ForeignKey(
"Machine",
null=True,
blank=True,
related_name="machine_engine",
on_delete=models.SET_NULL,
)
transmission_machine = models.ForeignKey(
"Machine",
null=True,
blank=True,
related_name="machine_transmission",
on_delete=models.SET_NULL,
)
# machine.py
class Machine(models.Model):
id = HashidSaltedAutoField(primary_key=True)
Generated SQL using latest iteration of the query:
SELECT
"machine"."id",
COALESCE((
SELECT
DISTINCT COUNT(U0."id") AS "count"
FROM
"part" U0
INNER JOIN "part_components" U1 ON
(U0."id" = U1."part_id")
INNER JOIN "component" U2 ON
(U1."component_id" = U2."id")
WHERE
((U2."machine_transmisssion_id" = "machine"."id"
OR U2."machine_engine_id" = "machine"."id")
AND U0."is_active")
GROUP BY
U0."id"
LIMIT 1),
0) AS "num_parts"
FROM
"machine"
Results from test:
# |------------+---------------------+----------------+--------------+--------------|
# | machine | components | parts | expected | output |
# | | | | num_parts | num_parts |
# |------------+---------------------+----------------+--------------+--------------|
# | r1 | f1 | ps1 | 2 | 1 |
# | | f2 | ps1 | | |
# | | | ps2 | | |
# |------------+---------------------+----------------+--------------+--------------|