Джанго комментирует на поле свойства - PullRequest
0 голосов
/ 04 сентября 2018

Я использую Django 2.0 и Django REST Framework

У меня есть модель, как показано ниже.

class Contact(models.Model):
    first_name = models.CharField(max_length=100)

class AmountGiven(models.Model):
    contact = models.ForeignKey(Contact, on_delete=models.PROTECT)
    amount = models.FloatField(help_text='Amount given to the contact')

    @property
    def total_payable(self):
        return self.amount

    @property
    def amount_due(self):
        returned_amount = 0
        for returned in self.amountreturned_set.all():
            returned_amount += returned.amount

        return self.total_payable - returned_amount

class AmountReturned(models.Model):
    amount_given = models.ForeignKey(AmountGiven, on_delete=models.CASCADE)
    amount = models.FloadField()

Мне нужно получить первые 10 контактов amount given и due соответственно.

На мой взгляд, я фильтрую данные как

@api_view(http_method_names=['GET'])
def top_ten(request):
    filter_type = request.query_params.get('type', None)

    if filter_type == 'due':
        # query for due type

    elif filter_type == 'given':
        qs = Contact.objects.filter(
            user=request.user
        ).values('id').annotate(
            amount_given=Sum('amountgiven__amount')
        ).order_by(
            '-amount_given'
        )[:10]

        graph_data = []
        for q in qs:
            d = {}
            contact = Contact.objects.get(pk=q['id'])

            d['contact'] = contact.full_name if contact else 'Unknown'
            d['value'] = q['amount_given']
            graph_data.append(d)

        return Response(graph_data)

    else:
        raise NotFound('No data found for given filter type')

запрос type может быть из-за или с учетом .

Код для типа given работает нормально, так как все поля находятся в базе данных. Но как я могу фильтровать на основе виртуального поля для due типа?

Что мне нужно сделать, это annotate Сумма из amount_due группы свойств по contact.

1 Ответ

0 голосов
/ 05 сентября 2018

Вы не можете фильтровать на основе @property.

Насколько я понимаю вашу проблему правильно, вы можете агрегировать сумму связанных AmountGiven и сумму AmountReturned, а затем вычислять поле due, в котором сохраняются результат вычитания буквы и формы.

Запрос:

from django.db.models import Sum, Value
from django.db.models.functions import Coalesce

Contact.objects.filter(  
    amountgiven__amount__gt=0
).annotate(
    due=Sum('amountgiven__amount') - Coalesce(Sum('amountgiven__amountreturned__amount'), Value(0))
).order_by('-due').values_list('due', 'id')

вернет:

<QuerySet [{'id': 3, 'due': 2500.0}, {'id': 1, 'due': 2450.0}, {'id': 2, 'due': 1500.0}]>

Однако с этим решением вы не сможете различить множество AmountGiven в одном Contact. Вы получаете большую картину как результаты.

Если вы хотите разделить из-за значения для AmountGiven экземпляра, просто аннотируйте так:

AmountGiven.objects.annotate(
    due=Sum('amount') - Coalesce(Sum('amountreturned__amount'), Value(0))
).order_by('-due').values_list('due', 'contact__id', 'id')

, который возвращает

<QuerySet [
   {'contact__id': 3, 'id': 3, 'due': 2500.0},
   {'contact__id': 1, 'id': 1, 'due': 1750.0},
   {'contact__id': 2, 'id': 2, 'due': 1500.0},
   {'contact__id': 1, 'id': 4, 'due': 350.0},
   {'contact__id': 1, 'id': 5, 'due': 350.0}
]> 

Ссылки

  1. Coalesce
...