Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a reverse Foreign Key in multiple tables with annotate

I have 3 models connected to one by a Foreign Key:

class A(models.Model):
    name = models.CharField(max_length=20)

class B(models.Model):
    a = models.ForeignKey(A, on_delete=models.CASCADE)
    amount_b = models.FloatField()

class C(models.Model):
    a = models.ForeignKey(A, on_delete=models.CASCADE)
    amount_c = models.FloatField()

class D(models.Model):
    a = models.ForeignKey(A, on_delete=models.CASCADE)
    amount_d = models.FloatField()

And I want to make a JOIN query based on the foreign keys using Django ORM to get the sum of each amount.

In SQL I would make something like:

SELECT 
  a.name, 
  b_sum,
  c_sum,
  d_sum
FROM A AS a
  LEFT JOIN (SELECT b.a, sum(amount_b) as b_sum
              from B as b
              group by b.a) as AB AB.a = a.id
  LEFT JOIN (SELECT c.a, sum(amount_c) as c_sum
              from C as c
              group by a) AS AC ON AC.a = a.id
  LEFT JOIN (SELECT d.a, sum(amount_d) as d_sum
              from D as d
              group by a) AS AD ON AD.a = a.id
WHERE a.name LIKE 'init%'

But I didn't find something like that on django ORM.

like image 752
Caio Kretzer Avatar asked Nov 03 '25 21:11

Caio Kretzer


1 Answers

You can do this

q = A.objects.annotate(
    b_sum=Subquery(
        B.objects.filter(a=OuterRef('pk'))
                 .values('a')
                 .annotate(b_sum=Sum('amount_b'))
                 .values('b_sum')
    ),
    c_sum=Subquery(
        C.objects.filter(a=OuterRef('pk'))
                 .values('a')
                 .annotate(c_sum=Sum('amount_c'))
                 .values('c_sum')
    ),
    d_sum=Subquery(
        D.objects.filter(a=OuterRef('pk'))
                 .values('a')
                 .annotate(d_sum=Sum('amount_d'))
                 .values('d_sum')
    )
)

It seems pretty complex query and also does not translate to same sql you mentioned in question, but it does the job.

Here is sql it generates.

SELECT "A"."id",
       "A"."name",
       (SELECT SUM(U0."amount_b") AS "b_sum" FROM "B" U0 WHERE U0."a_id" = ("A"."id") GROUP BY U0."a_id") AS "b_sum",
       (SELECT SUM(U0."amount_c") AS "c_sum" FROM "C" U0 WHERE U0."a_id" = ("A"."id") GROUP BY U0."a_id") AS "c_sum",
       (SELECT SUM(U0."amount_d") AS "d_sum" FROM "D" U0 WHERE U0."a_id" = ("A"."id") GROUP BY U0."a_id") AS "d_sum"
FROM "A"
like image 62
Nafees Anwar Avatar answered Nov 07 '25 07:11

Nafees Anwar



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!