优化Django查询

时间:2018-10-02 11:22:24

标签: python django django-models django-queryset

我有2个这样的模型:

class Client(models.Model):
  // some fields

class Transaction(models.Model):
  client = models.ForeignKey(Client)
  created = models.DateTimeField(auto_now_add=True)
  amount = DecimalField(max_digits=9, decimal_places=2)

仅当Transaction低于提供的created参数时,我想编写一个查询,为每个客户添加最后创建的date金额。

例如,如果我有一个像这样的数据集,并且提供的date是01/20:

Client1:
- Transaction 1, created on 01/15, 5€
- Transaction 2, created on 01/16, 6€
- Transaction 3, created on 01/22, 7€

Client2:
- Transaction 4, created on 01/18, 8€
- Transaction 5, created on 01/19, 9€
- Transaction 6, created on 01/21, 10€

Client3:
- Transaction 7, created on 01/21, 11€

然后,查询应返回15(交易2和交易5中的6€+ 9€)。

从性能的角度来看,我的目的是避免对N个客户端进行N个查询。

当前,我在选择正确的Transaction对象时遇到麻烦。 也许我可以从以下开始: Transaction.objects.filter(created__lt=date).select_related('client')。 但后来,我不知道如何仅选择每个客户端的最新版本。

2 个答案:

答案 0 :(得分:2)

看看aggregation上Django的文档,SumSubQuery expressionsQuerySet.values()的用法。通过这些,我们可以通过ORM构建单个查询以了解您要执行的操作,从而允许数据库完成所有工作:

from django.db.models import Sum, Subquery, OuterRef
from django.utils import timezone

from . import models


# first, start with the client list, rather than the transaction list    
aggregation = models.Client.objects.aggregate(
    # aggregate the sum of our per client sub queries
    result=Sum(
        Subquery(
            models.Transaction.objects.filter(
                # filter transactions by outer query's client pk
                client=OuterRef('pk'),
                created__lt=timezone.datetime(2018, 1, 20),
            )
            # order descending so the transaction we're after is first in the list
            .order_by('-created')
            # use QuerySet.values() to grab just amount and slice the queryset
            # to limit the subquery result to a single transaction for each client
            .values('amount')[:1]
        )
    )
)
# aggregation == {'result': Decimal('15.00')}

答案 1 :(得分:0)

使用以下方法应该可以解决使用Django latest QuerySet方法的问题

total = 0
for client in clients
    try:    
        total += Transactions.filter(client = client).filter(created__lt = date).latest('created').amount
    except DoesNotExist:
         pass