Использование поля свойства в аннотации в Django 2 - PullRequest
0 голосов
/ 31 октября 2018

Я использую Django 2.x и Django REST Framework.

У меня есть модель AmountGiven с полем свойства amount_due как

class AmountGiven(models.Model):
    contact = models.ForeignKey(Contact, on_delete=models.PROTECT)
    amount = models.FloatField(help_text='Amount given to the contact')
    interest_rate = models.FloatField(blank=True, default=None, null=True, help_text='% of interest to be calculated')
    total_due = models.FloatField(
        blank=True,
        default=0.0,
        editable=False
    )

    @property
    def interest_to_pay(self):
        if self.interest_rate:
            datetime_diff = datetime.now(get_localzone()) - datetime.combine(
                self.given_date, datetime.min.time(), get_localzone()
            )
            days = datetime_diff.days
            duration_in_year = days/365

            simple_interest_amount = (self.amount * duration_in_year * self.interest_rate)/100

            return simple_interest_amount

        return 0

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

    @property
    def amount_due(self):
        returned_amount = 0

        for returned in self.amountreturned_set.all():
            returned_amount += returned.amount

        total_due = self.total_payable - returned_amount

        self.total_due = total_due
        self.save()

        return total_due

Мне нужно сгенерировать данные графика для ответа Django REST Framework на общую сумму, подлежащую оплате для контакта.

def top_ten_due(request):
    qs = Contact.objects.filter(
        user=request.user
    ).values('first_name', 'last_name').annotate(
        total_due=Sum('amountgiven__total_due')
    ).order_by(
        '-total_due'
    )[:10]

    graph_data = []
    for q in qs:
        d = {
            'contact': q['first_name'] if q['first_name'] else '' + ' ' + q['last_name'] if q['last_name'] else '',
            'value': q['total_due'] if q['total_due'] else 0
        }
        graph_data.append(d)

    return Response(sorted(graph_data, key=lambda i: i['value'], reverse=True))

Причитающаяся сумма рассчитывается как свойство amount_due модели AmountGiven . И для того, чтобы сгенерировать данные графика, мне нужно аннотировать соответствующую сумму. Поскольку я не могу аннотировать поле свойства, у меня есть поле только для чтения total_due , которое обновляется каждый раз, когда вызывается свойство amount_due . и затем используется в аннотации.

Таким образом, каждый раз, когда я вызываю объект AmountGiven , модель обновляется и, таким образом, запускается сигнал post_save .

Теперь имеется приемник сигнала post_save , который используется для создания журнала при каждом обновлении суммы.

@receiver(post_save, sender=AmountGiven, dispatch_uid='amountgiven12345')
def amount_given_post_save_receiver(sender, instance, created, **kwargs):
    if created:
        action = 'given'
    else:
        action = 'updated'

    log = TransactionLog.objects.filter(
        user=instance.contact.user,
        contact=instance.contact,
        amount_given=instance
    ).order_by('-created').first()

    if not log or log.amount != instance.amount:
        TransactionLog.objects.create(
            user=instance.contact.user,
            contact=instance.contact,
            amount_given=instance,
            amount=instance.amount,
            action=action
        )

Хотя я использовал эту проверку, чтобы проверить, не существует ли уже журнал или его количество отличается от количества экземпляров. Каждый раз, когда список AmountGiven выбирается, он запускает post_save для каждого запроса и, таким образом, receive срабатывает, что создает журнал.

Таким образом, он создает много журналов за короткий промежуток времени.

Как мне справиться с такой проблемой?

Могу ли я использовать поле свойства amount_due в любом случае в аннотации, чтобы total_due не обновлялось каждый раз, предотвращая вызов post_save ?

...