Подсчет суммы объектов модели в сериализаторах django
В моем приложении у каждого пользователя есть свой кошелек, в котором он может сохранять свои ежедневные расходы.
Как я могу получить сумму денег для каждого экземпляра кошелька, в котором сохранено много расходов?
Я пробовал serializers.SerializerMethodField()
from django.db.models import Sum
from django.db.models import Q
class WalletInstanceSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.id')
entry = BudgetEntrySerializer(many=True, read_only=True)
expense_sum = serializers.SerializerMethodField()
class Meta:
model = WalletInstance
fields = '__all__'
def get_expense_sum(self, obj):
return WalletInstance.objects.filter(Q(id=obj.id)|Q(entry__entry_type='income')).aggregate(Sum('entry__amount'))['entry__amount__sum']
И это дает мне данные в правильном формате, но сумма неправильная, например
[
{
"id": "d458196e-49f1-42db-8bc2-ee1dba438953",
"owner": 1,
"entry": [
{
"id": 3,
"owner": 1,
"title": "dsfdsf",
"amount": 7,
"description": "sdfdsf",
"entry_type": "income",
"date_added": "2022-08-13",
"entry_category": {
"id": 2,
"name": "Transport"
}
},
{
"id": 4,
"owner": 1,
"title": "fesfvsdfgvbtdg",
"amount": 12,
"description": "efesf",
"entry_type": "income",
"date_added": "2022-08-13",
"entry_category": {
"id": 2,
"name": "Transport"
}
}
],
"viewable": [
"admin@gmail.com"
],
"expense_sum": 83,
"name": "dsdsds",
"date_added": "2022-08-13"
}
]
Сумма_расходов равна 83, но поля суммы равны 7 и 12, так что это равно 19
Как я могу получить правильное число?
Модели
class BudgetEntry(models.Model):
STATE= [
('income','income'),
('expenses','expenses'),
]
owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='owner_of_entry', on_delete=models.CASCADE)
title = models.CharField(max_length=20)
amount = models.IntegerField()
description = models.CharField(max_length=60, null=True)
entry_type = models.CharField(max_length=15, choices=STATE, null=True)
entry_category = models.ForeignKey(Category, null=True, blank=True, related_name='category_of_entry', on_delete=models.SET_NULL)
date_added = models.DateField(auto_now_add=True)
class WalletInstance(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique=True)
name = models.CharField(max_length=30, null=True)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='owner', on_delete=models.CASCADE)
viewable = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='can_view', blank=True)
entry = models.ManyToManyField(BudgetEntry, related_name='BudgetEntry', blank=True)
date_added = models.DateField(auto_now_add=True)
Я думаю, что ORM запрос для BudgetEntry
неправильный. Он должен выглядеть следующим образом:
def get_expense_sum(self, obj):
return obj.entry.filter(entry_type='income').aggregate(Sum('entry__amount'))['entry__amount__sum']
Я разобрался с этим. Сначала я создал @property в моей базе данных для получения суммы расходов, но это было не очень полезно, если бы я захотел посчитать доходы, поэтому я передал значение arg в функцию, где значение может быть расход/доход
class WalletInstance(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique=True)
name = models.CharField(max_length=30, null=True)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='owner', on_delete=models.CASCADE)
viewable = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='can_view', blank=True)
entry = models.ManyToManyField(BudgetEntry, related_name='BudgetEntry', blank=True)
date_added = models.DateField(auto_now_add=True)
def total_amount(self, value):
queryset = self.entry.filter(entry_type=value).aggregate(
total_amount=models.Sum('amount'))
return queryset["total_amount"]
Теперь я вызываю эту функцию в своем сериализаторе вместо того, чтобы считать там
class WalletInstanceSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.id')
entry = BudgetEntrySerializer(many=True, read_only=True)
viewable = serializers.SlugRelatedField(many=True, queryset=get_user_model().objects.all(), slug_field='email')
expense_sum = serializers.SerializerMethodField()
class Meta:
model = WalletInstance
fields = '__all__'
def get_expense_sum(self, obj):
wallet_obj = WalletInstance.objects.get(id=obj.id)
return wallet_obj.total_amount('expenses')