оптимизировать Django SQL запрос - PullRequest
0 голосов
/ 17 января 2019

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

У меня есть две модели

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)
    given_date = models.DateField(default=timezone.now)
    total_due = models.FloatField(blank=True, default=0.0, editable=False)

class AmountReturned(models.Model):
    amount_given = models.ForeignKey(AmountGiven, on_delete=models.CASCADE, blank=True)
    amount = models.FloatField()
    return_date = models.DateField(default=date.today)

Вариант использования

  • Может быть несколько записей о сумме, данной контакту
  • Может быть несколько записей о возвращенной сумме за данную сумму

Теперь яхочу получить total_due сумму для определенного контакта. Это включает

total_payable = total_given + interest 
total_due = total_payable - total_returned

Для вычисления total_due и interest я определил несколько свойства методов в AmountGiven модель.

@property
def interest_to_pay(self):
    if self.interest_rate:
        simple_interest_amount = ...
        return simple_interest_amount

    return 0

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

@property
def amount_due(self):
    total_due = self.total_payable - self.total_returned

    self.total_due = total_due
    self.save()

    return total_due

@property
def total_returned(self):
    returned_amount = self.amountreturned_set.aggregate(total_returned=Sum('amount'))['total_returned']
    if not returned_amount:
        returned_amount = 0

    return returned_amount

В Контакт модель, есть свойство метод для получения общей суммы задолженности для контакта.

@property
def amount_due(self):
    total_due = 0
    for due in self.amountgiven_set.all():
        total_due += due.amount_due

    return total_due

Запрос

ContactSerializer

class ContactMinSerializer(serializers.ModelSerializer):
    class Meta:
        model = Contact
        fields = (
            'id', 'first_name', 'amount_due', 'created', 'modified'
        )

Поскольку свойство amount_due используется в ContactSerializer, amount_due свойство вызывается каждый раз, когда контакт вызывается, и, следовательно, приводит к вложенным запросам к БД.

Как можно оптимизировать описанный выше сценарий в приложении, чтобы уменьшить количество запросов к БД при получениинг контакт или список контактов?Специально два свойства amount_due и total_returned .

amount_due () обновляет поле total_due в таблице каждый разэто называется.

Редактировать 2

class ContactViewSet(LoggingMixin, viewsets.ModelViewSet):
    serializer_class = ContactMinSerializer

    def get_queryset(self):
        return Contact.objects.filter(user=self.request.user).annotate(
            total_due=Sum(
                F('amountgiven_set__total_payable')
                - F('amountgiven_set__total_returned')
            )
        ).order_by('first_name')

1 Ответ

0 голосов
/ 19 января 2019

Вы ищете аннотации.

Ваш набор просмотра должен определять набор запросов следующим образом:

from django.db.models import F, Sum
Contact.objects.annotate(
    total_due=Sum(
        F('amountgiven_set__total_payable')
        - F('amountgiven_set__total_returned')
    )
)

Затем определите поле MethodSerializer на вашем сериализаторе, чтобы учесть его.

class ContactMinSerializer(serializers.ModelSerializer):
    total_due = serializers.SerializerMethodField()

    def get_total_due(self, obj):
        return return self.total_due

    class Meta:
        model = Contact
        fields = (
            'id', 'first_name', 'created', 'modified',
            'total_due',
        )
...