Home > Software design >  why getting Complex aggregates require an alias error?
why getting Complex aggregates require an alias error?

Time:10-03

I am working on Django where I have two models Gigs and Orders and I am calculating average Completion time of order of every gig.

in order model I have two fields order start time (which I'm sending whenever seller accepts the order) and order completed time (which I'm sending when seller delivered) the order.

but the problem is that if I have data in Orders table related to some gig and I retrieve the gig then it worked perfectly but if I tries to retrieve a gig with no orders yet ( in orders table there is no record/field containing that item/gig) then it gives me following error

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

Example

for example if we have following data in orders table where item is foreign key from gigs table order table Now if I retrieve gig 22 it worked well and gives me all the fields in Api including average completion time but if I want to get gig 23 or any other it gives me error

but I want it should return all other fields same and average completion time : null

full Trackback of error

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

CodePudding user response:

As @Iain Shelvington has mentioned, you can set a name to the aggregate like this to resolve the error:

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

And additionally, since in the get_queryset it can potentially already have a calculated value, you can try to change getattr to hasattr and just 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']

This is to check if _average_completionTime is already calculated for a gig instance, to prevent calculating it again.

  • Related