【问题标题】:Get latest payment from related model in Django Admin从 Django Admin 中的相关模型获取最新付款
【发布时间】:2021-06-06 13:21:52
【问题描述】:

我想在 Django 管理中使用“前提”模型输出一个表。此外,我想在此表的附加列中包含输出,例如“最后一次公用事业付款”。它实际上是相关表中的一列。数据库中可能没有付款,因此管理员应该能够看到一个空单元格或付款​​日期。

我能够编写一个数据库查询来显示我需要的信息。其重要和工作的部分如下:

SELECT jp.id,
       jp.number apartment,
       jp.building_number building,
       jp.rent,
       jp.arrears,
       jpm.last_payment
FROM jasmin_premises jp
LEFT JOIN (
        SELECT pm.premises_id,
               max(pm.paid) last_payment
        FROM jasmin_payment pm
        GROUP BY pm.premises_id
    ) jpm ON jp.id = jpm.premises_id;

并且输出类似于以下内容:

id  | apartment | building | rent   | arrears | last_payment
--------------------------------------------------------------
170 | 1X        | 6-A      | 297.43 | 2.57,   | NULL
72  | 2         | 4        | 289.66 | -678.38 | 2021-01-31
173 | 3Z        | 7        | 432.86 | 515.72  | 2021-02-04
73  | 4         | 8-B      | 292.25 | 515.44  | 2021-02-04
74  | 5         | 8-B      | 112.42 | 3249.34 | NULL
75  | 6A        | 122      | 328.48 | 386.23  | 2021-02-04
76  | 7         | 42       | 482.06 | 964.12  | 2021-01-31
77  | 8         | 1        | 433.71 | 867.42  | 2021-01-31
78  | 9C        | 12       | 322.79 | 322.79  | 2021-02-04
79  | 10        | 122      | 324.22 | 0       | 2021-02-04
80  | 12        | 12       | 322.79 | 1232.46 | NULL
81  | 14        | 5-Z      | 440.82 | 978.44  | 2021-02-04

我正在使用以下模型(仅占重要部分):

class Premises(models.Model):
    number = models.CharField(
        blank=False,
        null=False,
        max_length=10)

    building_number = models.CharField(
        blank=False,
        null=False,
        max_length=3)

    rent = models.DecimalField(
        blank=False,
        null=False,
        max_digits=12,
        decimal_places=2,
        default=0.0)

    area = models.DecimalField(
        blank=False,
        null=False,
        max_digits=5,
        decimal_places=2)

class Payment(models.Model):
    paid = models.DateField(
        blank=False,
        null=False)

    premises = models.ForeignKey(
        Premises,
        blank=False,
        null=False,
        on_delete=models.CASCADE,
        related_name='payments',
        db_index=True)

有没有办法覆盖admin.ModelAdmin.get_queryset(例如使用注释)来获得一个额外的列,就像我上面的例子一样?有没有其他方法可以使用 Django ORM 对复合 DB 查询进行 LEFT JOIN?

【问题讨论】:

  • 您好,先生,我不知道在 django admin 中显示这个是否是最好的方式,因为它变得更加复杂,我认为您应该创建一个 html 模板来显示这些信息,如果您选择了这个最后一个选项,我会帮你的。
  • @Luiz 问题是使用 Django ORM 进行单个数据库查询。在这种情况下,它是管理站点还是其他地方并不那么重要

标签: django django-models django-admin django-queryset django-orm


【解决方案1】:

要在 django 中进行此查询,您必须将 models.Manager() 添加到表中,如下所示:

models.py

class Premises(models.Model):
    # existent code
    objects = models.Manager()


class Payment(models.Model):
    # existent code
    objects = models.Manager()

在您要访问此信息的应用程序部分

from .models import Premises, Payment

premises = Premises.objects.all()
data_to_display = []
for premise in premises:
    payments = Payment.objects.filter(premises=premise).order_by('-paid')
    if len(payments) == 0:
        last_payment = "Null"
    else:
        last_payment = payments[0]
    object_to_list = {
        "id": premise.id,
        "apartment": premise.number,
        "building": premise.building_number,
        "rent": premise.rent,
        "arreaars": premise.area,
        "last_payment": last_payment.paid
    }
    data_to_display.append(object_to_list)

【讨论】:

  • 这实际上不是一个坏方法。但是,不适合我的是,此代码将对数据库进行与表中记录一样多的查询。但我想进行一个数据库查询,以从相关表中获取所有带有附加列的记录
  • @serghei 所以我认为唯一的方法是进行原始查询docs.djangoproject.com/en/3.1/topics/db/sql 这是非常好的工具
【解决方案2】:

解决方案是使用 Subquery 表达式将显式子查询添加到 QuerySet。我们还需要使用 OuterRef,因为 子查询 中的查询集需要引用外部查询中的字段。

那么让我们创建一个子查询:

from django.db.models import OuterRef

payments = Payment.objects.filter(
    premises=OuterRef('pk')
).order_by('-paid')

下一步是将payments 子查询传递给查询集:

from django.db.models import Subquery

# 'payments' here is from example above
premises = Premises.objects.annotate(
    last_payment=Subquery(payments.values('paid')[:1])
)

最后,让我们看看使用 SQL 来查询数据库中的对象行:

print(premises.query)

(输出已格式化,仅显示重要部分)

SELECT "jasmin_premises"."id",
       "jasmin_premises"."number",
       "jasmin_premises"."building_number",
       "jasmin_premises"."arrears",
       "jasmin_premises"."rent",
       (SELECT U0."paid"
        FROM "jasmin_payment" U0
        WHERE U0."premises_id" = "jasmin_premises"."id"
        ORDER BY U0."paid" DESC
        LIMIT 1) AS "last_payment"
FROM "jasmin_premises";

现在,在执行测试之后,我们可以在 ModelAdmin 中使用它:

from django.contrib import admin
from django.db.models import OuterRef, Subquery

from .models import Payment, Premises


@admin.register(Premises)
class PremisesAdmin(admin.ModelAdmin):

    list_display = (
        'number',
        'building_number',
        'rent',
        'arrears',
        'last_payment',
    )

    def get_queryset(self, request):
        qs = super().get_queryset(request)

        payments = Payment.objects.filter(
            premises=OuterRef('pk')
        ).order_by('-paid')

        qs = qs.annotate(
            last_payment=Subquery(payments.values('paid')[:1]),
        )

        return qs

    def last_payment(self, obj):
        return obj.last_payment
    last_payment.short_description = 'Last payment'
    last_payment.admin_order_field = 'last_payment'

好吧,这不使用 JOIN,但这种方法会强制 Django 执行子查询。

也许在某些情况下,可以编写一个等效的查询集来更清晰、更高效地执行相同的任务,但是,这是我迄今为止取得的最好成绩。

【讨论】:

    猜你喜欢
    • 2018-05-25
    • 1970-01-01
    • 2013-02-17
    • 2019-10-10
    • 2014-11-02
    • 2021-11-01
    • 2017-11-28
    • 1970-01-01
    • 2014-11-03
    相关资源
    最近更新 更多