【问题标题】:How to make a reverse Foreign Key in multiple tables with annotate如何使用注释在多个表中创建反向外键
【发布时间】:2020-06-01 23:49:06
【问题描述】:

我有 3 个模型通过外键连接到一个:

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()

我想使用 Django ORM 根据外键进行 JOIN 查询,以获取每个金额的总和。

在 SQL 中,我会做类似的事情:

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%'

但我在 django ORM 上没有找到类似的东西。

【问题讨论】:

    标签: django django-models


    【解决方案1】:

    B.amount_bD.amount_dCharFields,如果它们不包含数值,对这些字段执行求和可能会导致问题。您应该考虑将它们更改为数字字段

    类似下面的东西会给你一个 A 对象的查询集,用这些和注释

    queryset = A.objects.annotate(
        b_sum=Sum('b__amount_b'),  # It's either "b" or "b_set"
        c_sum=Sum('c__amount_c'),
        d_sum=Sum('c__amount_d'),
    )
    for a in queryset:
        print(a.name, a.b_sum, a.c_sum, a.d_sum)
    

    【讨论】:

    • 是的,我忘记将模型上的字段更改为 FloatField。我会试试这个。
    • 它不起作用:'(我以前已经尝试过类似的东西。无论如何谢谢。
    【解决方案2】:

    你可以这样做

    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')
        )
    )
    

    这似乎是相当复杂的查询,也不能转换为您提到的相同 sql,但它可以完成工作。

    这是它生成的 sql。

    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"
    

    【讨论】:

    • 这就是我所需要的!谢谢
    【解决方案3】:

    如果您发现您的查询对于 DJango ORM 来说很复杂,那么有一些方法可以直接执行原始查询。

    >>> for p in Person.objects.raw('SELECT * FROM myapp_person'):
    ...     print(p)
    John Smith
    Jane Jones
    

    这个代码 sn-p 是从 DJango 网站本身挑选出来的。 完整网址如下。

    https://docs.djangoproject.com/en/3.0/topics/db/sql/

    给出的链接还包含自定义查询选项和光标等,以帮助您解决问题。

    【讨论】:

      猜你喜欢
      • 2014-07-22
      • 1970-01-01
      • 2019-02-28
      • 2020-04-09
      • 2021-08-19
      • 2015-05-09
      • 2017-06-28
      • 1970-01-01
      • 2023-04-01
      相关资源
      最近更新 更多