为什么获取复杂聚合需要别名错误?

why getting Complex aggregates require an alias error?

我在 Django 上工作,我有两个模型 GigsOrders 我正在计算每个演出订单的平均完成时间.

order 模型中我有两个字段 order start time (只要卖家接受订单我就会发送)和 订单完成时间(我在卖家发货时发送)订单。

但问题是,如果我在订单 table 中有与某个演出相关的数据,并且我检索了该演出,那么它工作得很好,但如果我尝试检索还没有订单的演出(在订单 table 没有 record/field 包含 item/gig) 然后它给了我以下错误

Complex aggregates require an alias

Models.py

class Gigs(models.Model):
    title = models.CharField(max_length=255)
    category = models.ForeignKey(Categories , on_delete=models.CASCADE)
    images = models.ImageField(blank=True, null = True, upload_to= upload_path)
    price = models.DecimalField(max_digits=6, decimal_places=2)
    details = models.TextField()
    seller = models.ForeignKey(User,default=None, on_delete=models.CASCADE)
    
    @property
    def average_completionTime(self):
        if getattr(self, '_average_completionTime', None):
            return self._average_completionTime
        return self.gig.aggregate(Avg(F('orderCompletedTime') - F('orderStartTime')))

class Orders(models.Model):
    buyer = models.ForeignKey(User,default=None, on_delete=models.CASCADE,related_name='buyer_id')
    seller = models.ForeignKey(User,default=None, on_delete=models.CASCADE,related_name='seller_id')
    item = models.ForeignKey(Gigs,default=None, on_delete=models.CASCADE,related_name='gig')
    payment_method= models.CharField(max_length=10)
    address = models.CharField(max_length=255)
    mobile = models.CharField(max_length=13,default=None)
    quantity = models.SmallIntegerField(default=1)
    status = models.CharField(max_length=13,default='new order')
    orderStartTime = models.DateTimeField(default=timezone.now)
    orderCompletedTime = models.DateTimeField(default=timezone.now)
    isCompleted = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)

Views.py

class RetrieveGigsAPI(GenericAPIView, RetrieveModelMixin):
    def get_queryset(self):
        return Gigs.objects.annotate(
            _average_completionTime=Avg(
                ExpressionWrapper(F('gig__orderCompletedTime') - F('gig__orderStartTime'), output_field=DurationField()),
                filter=Q(gig__isCompleted=True),
            )
        )
    serializer_class = GigsSerializerWithAvgTime
    permission_classes = (AllowAny,)

    def get(self, request , *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

Serializers.py

class GigsSerializerWithAvgTime(serializers.ModelSerializer):
    average_completionTime = serializers.SerializerMethodField()
    def get_average_completionTime(self, obj):
        return obj.average_completionTime
    class Meta:
        model = Gigs
        fields = ['id','title','category','price','details','seller','images','average_completionTime']

例子

例如,如果我们在订单 table 中有以下数据,其中项目是演出中的外键 table 现在,如果我检索演出 22,它运行良好,并为我提供 Api 中的所有字段,包括平均完成时间,但如果我想获得演出 23 或任何其他,它会给我错误

但我希望它应该 return 所有其他字段相同且平均完成时间:null

错误的完整引用

Environment:


Request Method: GET
Request URL: http://localhost:8000/seller/GigApi/23/

Django Version: 3.0.11
Python Version: 3.9.1
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'rest_framework',
 'djoser',
 'accounts',
 'adminuser',
 'seller',
 'buyer',
 'corsheaders']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'corsheaders.middleware.CorsMiddleware',
 'django.middleware.common.CommonMiddleware']



Traceback (most recent call last):
  File "C:\Python39\lib\site-packages\django\db\models\query.py", line 374, in aggregate
    arg.default_alias
  File "C:\Python39\lib\site-packages\django\db\models\aggregates.py", line 65, in default_alias
    raise TypeError("Complex expressions require an alias")

During handling of the above exception (Complex expressions require an alias), another exception occurred:
  File "C:\Python39\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
    response = get_response(request)
  File "C:\Python39\lib\site-packages\django\core\handlers\base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "C:\Python39\lib\site-packages\django\core\handlers\base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Python39\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "C:\Python39\lib\site-packages\django\views\generic\base.py", line 71, in view
    return self.dispatch(request, *args, **kwargs)
  File "C:\Python39\lib\site-packages\rest_framework\views.py", line 509, in dispatch
    response = self.handle_exception(exc)
  File "C:\Python39\lib\site-packages\rest_framework\views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "C:\Python39\lib\site-packages\rest_framework\views.py", line 480, in raise_uncaught_exception
    raise exc
  File "C:\Python39\lib\site-packages\rest_framework\views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)
  File "C:\FYP\backend\seller\views.py", line 76, in get
    return self.retrieve(request, *args, **kwargs)
  File "C:\Python39\lib\site-packages\rest_framework\mixins.py", line 56, in retrieve
    return Response(serializer.data)
  File "C:\Python39\lib\site-packages\rest_framework\serializers.py", line 548, in data
    ret = super().data
  File "C:\Python39\lib\site-packages\rest_framework\serializers.py", line 246, in data
    self._data = self.to_representation(self.instance)
  File "C:\Python39\lib\site-packages\rest_framework\serializers.py", line 515, in to_representation
    ret[field.field_name] = field.to_representation(attribute)
  File "C:\Python39\lib\site-packages\rest_framework\fields.py", line 1870, in to_representation
    return method(value)
  File "C:\FYP\backend\seller\serializers.py", line 21, in get_average_completionTime
    return obj.average_completionTime
  File "C:\FYP\backend\seller\models.py", line 29, in average_completionTime
    return self.gig.aggregate(Avg(F('orderCompletedTime') - F('orderStartTime')))
  File "C:\Python39\lib\site-packages\django\db\models\manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "C:\Python39\lib\site-packages\django\db\models\query.py", line 376, in aggregate
    raise TypeError("Complex aggregates require an alias")

Exception Type: TypeError at /seller/GigApi/23/
Exception Value: Complex aggregates require an alias

正如@Iain Shelvington 所提到的,您可以像这样为聚合设置一个名称来解决错误:

return self.gig.aggregate(average_completion=Avg(F('orderCompletedTime') - F('orderStartTime')))['average_completion']
                          # ^^^ Add this

此外,由于在 get_queryset 中它可能已经有一个计算值,您可以尝试将 getattr 更改为 hasattr 并且只是 return:

    @property
    def average_completionTime(self):
        if hasattr(self, '_average_completionTime'):
            return self._average_completionTime

        return self.gig.aggregate(
            average_completion=Avg(F('orderCompletedTime') - F('orderStartTime'))
        )['average_completion']

这是为了检查 _average_completionTime 是否已经为 gig 实例计算,以防止再次计算它。