简体   繁体   中英

Django's NotImplementedError: aggregate() + distinct(fields) not implemented

I have quite simple models:

class Profile(models.Model):
    name = models.CharField(max_length=100, unique=True)
    age = models.IntegerField(default=18)


class Place(models.Model):
    name = models.CharField(max_length=100, blank=True, null=True)
    address = models.CharField(max_length=100, blank=True, null=True)


class ProfilePlaceFeedback(models.Model):
    profile = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='feedback_set')
    place = models.ForeignKey(Place, on_delete=models.CASCADE, related_name='feedback_set')
    review = models.TextField(blank=True, null=True)
    rating = models.IntegerField(default=0)
    timestamp = models.DateTimeField(auto_now_add=True)

ProfilePlaceFeedback - is model to store each rating left by user. And to count the rating for some place , I need to retrieve all LATEST User feedbacks and sum up all rating values. Here's the code for retrieving all last feedbacks by each user:

place.feedback_set.order_by('profile', '-timestamp').distinct('profile')

But making querying:

place.feedback_set.order_by('profile', '-timestamp').distinct('profile').aggregate(Sum(rating))

Raises exception:

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/PATH_TO_VIRTUALENV/lib/python3.6/site-packages/django/db/models/query.py", line 357, in aggregate
    raise NotImplementedError("aggregate() + distinct(fields) not implemented.")
NotImplementedError: aggregate() + distinct(fields) not implemented.

Using Django 2.0 and postgresql as database.

Please help me with this problem :)

I guess you are looking for aggregate data like this:

from django.db.models import F

Profile
.objects
.values(profile_name=F('name'),            
        place_name=F('profileplacefeedback__place__name'),
        )
.annotate(timestamp=Max('profileplacefeedback__timestamp'),
          rating=Sum('profileplacefeedback__rating'))
.order_by( 'profile_name', '-timestamp')

If you need more data, just add it on values section.

Disclaimer: query not tested here, just wrote by hand. Let me know if it fails to fix it or delete the answer (in order to help future users)

Edited After OP Comment:

I need to calculate an average rating for a given place and filtering only last rating left by profile.

You are looking for FirstValue of django window functions .

from django.db.models import Avg,  F, RowRange, Window
from django.db.models.functions.window import FirstValue

q=( Place
   .objects
   .annotate(
        avg_rating=Window(
            expression=FirstValue('feedback_set__rating'),
            partition_by=[F('feedback_set__profile'),
                          F('feedback_set__place')],
            order_by=F('feedback_set__timestamp').desc()
        )
     )
    .values(
        place_name=F('feedback_set__place__name'),
     )
    .annotate(
        rating=Avg('avg_rating')
     )
)

Underlying SQL:

>>> print (q.query)

SELECT
   T4."name" AS "place_name",
   AVG(FIRST_VALUE("a_profileplacefeedback"."rating") 
       OVER (PARTITION BY "a_profileplacefeedback"."profile_id", 
                          "a_profileplacefeedback"."place_id" 
             ORDER BY "a_profileplacefeedback"."timestamp" DESC)) AS "rating" 
FROM
   "a_place" 
   LEFT OUTER JOIN
      "a_profileplacefeedback" 
      ON ("a_place"."id" = "a_profileplacefeedback"."place_id") 
   LEFT OUTER JOIN
      "a_place" T4 
      ON ("a_profileplacefeedback"."place_id" = T4."id") 
GROUP BY
   T4."name"

看来您需要这个或类似的东西:

place.feedback_set.values('profile').annotate(rating_sum=Sum(rating)).values('profile','rating_sum').order_by('profile')

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM