na5tyk
na5tyk

Reputation: 181

Sum data in serializer Django

I would like sum data from nested object.

My model:

class Diet(models.Model):
    day = models.IntegerField()

    meals = models.ManyToManyField(Meal)
    user = models.ForeignKey(User, on_delete=models.CASCADE)

    def __str__(self):
        return f'{self.day} - {self.user}'

My viewset:

class DietViewSet(viewsets.ModelViewSet):
    queryset = Diet.objects.all()
    serializer_class = DietSerializer
    pagination_class = None

    def get_queryset(self):
        if self.action == 'list':
            self.queryset = Diet.objects.filter(user=self.request.user).order_by('day')
        else:
            self.queryset = Diet.objects.all().order_by('day')
        return super(DietViewSet, self).get_queryset()

    def get_permissions(self):
        if self.action in ['list', 'retrieve']:
            self.permission_classes = [permissions.AllowAny]
        else:
            self.permission_classes = [IsDietician]
        return super(DietViewSet, self).get_permissions()

My serializers:

class MealSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Meal
        fields = '__all__'


class DietSerializer(serializers.ModelSerializer):
    def to_representation(self, instance):
        result = super(DietSerializer, self).to_representation(instance)
        result['meals'] = MealSerializer(instance.meals, many=True).data

        return result

    class Meta:
        model = models.Diet
        fields = '__all__'

Now I would like add total_kcal which will be calculate by data from meals. I tried did it by kcal = serializers.MethodSerializerField()

and

def get_kcal(self, obj)

but I don't know how can I get data from serialized objects in to_representation.

Example response:

[
   {
      "id":1,
      "day":1,
      "user":1,
      "meals":[
         {
            "id":4,
            "name":"Rise",
            "description":"",
            "kcal":150.0,
            "protein":3.0,
            "fat":0.0,
            "carbs":28.0
         },
         {
            "id":5,
            "name":"Chocoloate",
            "description":"",
            "kcal":200.0,
            "protein":0.0,
            "fat":0.0,
            "carbs":100.0
         }
      ]
   }
]

Upvotes: 1

Views: 1528

Answers (2)

na5tyk
na5tyk

Reputation: 181

Thank you Arakkal Abu but I found additional method to do it ;)

I added to my model:

@property
def total_kcal(self):
    qs = self.meals.through.objects.all().aggregate(total_kcal=models.Sum('meal__kcal'))
    return qs['total_kcal']

and change fields in DietSerializr to:

fields = ['day', 'type', 'meals', 'user', 'total_kcal']

Upvotes: 1

JPG
JPG

Reputation: 88569

You can use Python's list comprehension here as

class DietSerializer(serializers.ModelSerializer):
    def to_representation(self, instance):
        result = super(DietSerializer, self).to_representation(instance)
        result['meals'] = MealSerializer(instance.meals, many=True).data

        # You can use the list comprehension here as
        result["kal_sum"] = sum([meal["kcal"] for meal in result['meals']])

        return result

    class Meta:
        model = models.Diet
        fields = '__all__'

Upvotes: 3

Related Questions