Django - Select nested collection Avg with group b

2019-07-30 01:06发布

问题:

I have the following models, and what I would like is to select a collection of businesses, each of them with a collection of AVG(rate) based on group by review_question_id.

Here are the necessary models:

class ReviewQuestion(models.Model):
    """Represents a question to be given in a business type
    """
    business_type = models.ForeignKey(BusinessType, on_delete=models.CASCADE)
    question_text = models.CharField(max_length=100)


class Business(models.Model):
    """Values for a specific business, based on a type
    will inherit questions and reviews
    """
    business_type = models.ForeignKey(BusinessType, on_delete=models.CASCADE)
    name = models.CharField(max_length=100)


class CustomerReview(models.Model):
    """Rate value by customers on a specific quesiton
    for a specific business
    """
    business = models.ForeignKey(Business, on_delete=models.CASCADE)
    review_question = models.ForeignKey(
        ReviewQuestion, on_delete=models.CASCADE)
    review_value = models.PositiveSmallIntegerField()

The nearest query I tried to get close on this:

items = Business.objects.filter(business_type_id=type_id).values(
        'id', 'name', 'business_type_id', 'address', 'customerreview__review_question_id').annotate(rate=Avg('customerreview__review_value'))

and the problem with it is duplication. It duplicates the entire list and the result is flat, sort of what you get when you write it in flat tsql.

The ideal result would look something like:

[
    {
        "business_id": 1,
        "business_name": "something",
        "rating":[
            {
                "Question_1":{
                    "title":"ReviewQuestion__question_text",
                    "Avg":5.0
                },
                "Question_2":{
                    "title":"ReviewQuestion__question_text",
                    "Avg":5.0
                },{
                    ...
                }
            }
        ]
    }
]

Any help would be appreciated as I'm new to python/django.

回答1:

If you use django-rest-framework it will be easy. You should use two serializers.

from rest_framework import serializers

class BussnessSerializer(serializers.ModelSerializer):
     rating = QuestionWithRatingSerializer(
                  source='reviewquestion_set', 
                  many=True
     )
     business_id = serializers.IntegerField(source='id')
     business_name = serizlizers.CharField(source='name')


     class Meta:
           model = Business
           fields = ('rating', 'business_id', 'business_name')


class QuestionWithRatingSerializer(serializers.ModelSerializer):
      title = serializers.CharField(source='question_text')
      avg = serizlizers.SerializerMethodField()

      def get_avg(self, obj):

          return obj.customerreview_set.aggregave(
                     avg=Avg('review_value')['avg'])

      class Meta:
           model = ReviewQuestion
           fields = ('title', 'avg')