Django:复杂的注释/预取查询

时间:2018-02-14 20:16:40

标签: django

假设我有以下Django模型:

class Account(models.Model):
    name = models.CharField()


class Payment(models.Model):
    account = models.ForeignKey(Account)
    amount = models.FloatField()
    date = models.DateField()
    person = models.ForeignKey(User)

我有各种帐户,各种人都可以在帐户中付款。我想显示一个帐户列表,并显示谁支付了最后一笔款项,金额和日期是多少。像这样的东西(不知道如何在这里添加一个合适的表):

帐户|最后一笔付款由|上次付款日期

食物|彼得| 2018年2月3日

饮料|约翰| 2018年2月10日

如何通过一次或两次查询实现此目的?也许通过一些花哨的预取/注释,但我无法弄明白。我可以使用注释+最大值来获取最后一个日期,但我如何获得付款人?

否则我只需要查询每一行。如果我将帐号的数量限制为50,那么我认为50个查询不是很糟糕,但仍然不理想。

2 个答案:

答案 0 :(得分:2)

如果Payment ID是连续的并且它始终与日期字段匹配,那么从某种意义上说,最新付款总是获得最新的ID,您可以使用两个查询来执行此操作:

from django.db.models import Max

last_payments_ids = Payment.objects \
    .values('account_id') \
    .annotate(last_payment_id=Max('id')) \
    .values_list('last_payment_id', flat=True)

last_payment_per_account = Payment.objects \
    .select_related('account', 'person') \
    .filter(id__in=last_payments_ids)

请注意,我使用account_id按表达式创建了一个组,然后Max('id')应用于Payment的ID,而不是Account 1}}。

只需两个查询,无论记录多少:

{% for payment in last_payment_per_account %}
    <td>{{ payment.account.name }}</td>
    <td>{{ payment.person.first_name }}</td>
    <td>{{ payment.date }}</td>
{% endfor %}

答案 1 :(得分:0)

根据您正在处理的记录数量(例如,除非数百万),我认为不需要任何注释或自定义SQL:

recent_payments = Payment.objects.order_by('-date').select_related("account")

或者如果您想要上一个付款对象,可以按latest()

获取
recent_payments = Payment.objects.order_by('-date').latest().select_related("account")

然后直接浏览结果:

for payment in recent_payments:
  col1 = payment.account.name  # eg "Food"
  col2 = payment.person.first_name  # eg "John"
  col3 = payment.date.strftime("-%d %B %Y")  # eg "10 Feb 2018"

请记住将db_index = True添加到日期字段,因为您要对该字段进行排序,并且在未编制索引的字段上排序要慢得多。

编辑:道歉,我误读了最初的问题,认为这是所有付款的记录。上述答案优雅地完成了OP最初要求的工作。