【问题标题】:Queryset with annotate Subquery with OuterRef and Sum使用 OuterRef 和 Sum 注释子查询的查询集
【发布时间】:2021-09-17 19:10:21
【问题描述】:

我正在尝试从子查询中的另一个模型返回字段的总和。

我的主要查询集返回公司类型的所有用户。我必须通过从 CreditOrder 中获取数据并汇总 credit_used 字段来返回使用的积分总数。

我正在使用来自 django-modelcluster 的 ClusterableModel 和 ParentalKey

我的CreditOrder 模特

class CreditOrder(ClusterableModel):
    credit = ParentalKey(
        Credit, on_delete=models.CASCADE, related_name="credit_order"
    )
    order = ParentalKey(Order, on_delete=models.CASCADE, related_name="credit_order")
    credit_used = models.DecimalField(
        max_digits=12, decimal_places=2, null=True, blank=True
    )

我的User 模特

class User(AbstractUser, ClusterableModel):
    username = models.CharField(max_length=40, null=True, blank=True)
    user_type = models.CharField(max_length=20, choices=TIPO_UTENTE, default="dipendente")

我的查询集使用类模型 User

    def get_queryset(self, request):
        qs = super().get_queryset(request)
        qs = qs.filter(user_type='company')
        credits_used_subquery = Subquery(CreditOrder.objects.filter(credit__font__company__id=OuterRef('id')).order_by()
                    .values('credit_used').annotate(credit_used_sum=Sum('credit_used'))
                    .values('credit_used_sum'), output_field=DecimalField())
        qs = qs.annotate(
            _credits_used_sum=credits_used_subquery
        )
        return qs

但是这个错误正在返回我:

django.db.utils.ProgrammingError: more than one row returned by a subquery used as an expression

【问题讨论】:

  • 欢迎来到 Stack Overflow。请参阅如何写一个minimal reproducible example。在您的问题中,特别是很少有事情令人困惑什么是ClusterableModelParentalKey??你的get_queryset 方法处理的是什么模型类?问题很可能是因为错误表明您的子查询返回多行。您可以通过将其切片 [:1] 来限制它,这可能会有所帮助,但取决于您想要的结果可能会给出错误的结果。
  • 您好阿卜杜勒,感谢您的反馈。我用更多细节编辑了这篇文章。不幸的是我不能分裂,所有的物体和它们的总和都为我服务。我也尝试执行此子查询:CreditOrder.objects.filter(credit__font__company__id =OuterRef('id')).aggregate(credit_used_sum=Sum('credit_used')),但我收到此错误:此查询集包含对外部的引用查询并且只能在子查询中使用。

标签: python sql django django-queryset django-database


【解决方案1】:

如果您只需要总结公司使用的所有积分,您可以这样做:

qs.annotate(_credits_used_sum=Sum('font__credit__credit_used'))

【讨论】:

    【解决方案2】:

    如果不查看 CreditOrder 和 User 之间的所有模型,就很难准确判断出你做错了什么。看起来 Credit 模型链接到 Font 并且 Font 可能有一个名为 Company 的属性,它是 User 模型的外键?

    无论如何,您的第一个 values 调用有错误的参数,您需要按照与外部引用中链接的基本相同的内容进行分组。所以我建议

    .values('credit__font__company__id')
    

    在第一次调用值时。并保持注释和第二次调用值相同。

    另一个答案建议使用连接而不是子查询进行求和,如果您喜欢该 api 的简单性,但仍想使用子查询,您可以使用 django-sql-utils 包。在你之后pip install django-sql-utils

    from sql_util.utils import SubquerySum
    
    qs.annotate(_credits_used_sum=SubquerySum('font_credit_credit_used')
    

    【讨论】:

      【解决方案3】:

      我用这门课解决了我的问题:

      class SubquerySum(Subquery):
          template = "(SELECT COALESCE(SUM(%(field)s), %(zero_value)s) FROM (%(subquery)s) _sum)"
      
          def as_sql(self, compiler, connection, template=None, **extra_context):
              if 'field' not in extra_context and 'field' not in self.extra:
                  if len(self.queryset._fields) > 1:
                      raise FieldError('You must provide the field name, or have a single column')
                  extra_context['field'] = self.queryset._fields[0]
              if 'zero_value' not in extra_context and 'zero_value' not in self.extra:
                  extra_context['zero_value'] = 0
              return super().as_sql(compiler, connection, template=template, **extra_context)
      

      def get_queryset(self, request):
          credit_query=CreditOrder.objects.filter(credit__font__company__id=OuterRef('id')).order_by()
                  .values('credit_used')
          qs = super().get_queryset(request)
          qs = qs.filter(user_type='company')
          qs = qs.annotate(
              _credits_used_sum=SubquerySum(credit_query, zero_value=0, field='credit_used')
              )
          return qs
      

      【讨论】:

        猜你喜欢
        • 2018-04-16
        • 2022-01-19
        • 2020-01-18
        • 1970-01-01
        • 2013-06-22
        • 1970-01-01
        • 2017-10-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多