1

我一直在玩 Django ORM 中的新聚合功能,我认为有一类问题应该是可能的,但我似乎无法让它工作。此处描述了我尝试生成的查询类型。

所以,假设我有以下模型 -

class ContactGroup(models.Model):
    .... whatever ....

class Contact(models.Model):
    group = models.ForeignKey(ContactGroup)
    name = models.CharField(max_length=20)
    email = models.EmailField()
...

class Record(models.Model):
    contact = models.ForeignKey(Contact)
    group = models.ForeignKey(ContactGroup)
    record_date = models.DateTimeField(default=datetime.datetime.now)

    ... name, email, and other fields that are in Contact ...

因此,每次创建或修改联系人时,都会创建一个新记录,以保存当时出现在联系人中的信息以及时间戳。现在,我想要一个查询,例如,返回与 ContactGroup 关联的每个联系人的最新记录实例。在伪代码中:

group = ContactGroup.objects.get(...)
records_i_want = group.record_set.most_recent_record_for_every_contact()

一旦我弄清楚了这一点,我只想能够在查询集上抛出一个filter(record_date__lt=some_date),并获取它存在于some_date.

有人有什么想法吗?

编辑:看来我并没有真正让自己清楚。使用这样的模型,我想要一种使用纯 django ORM(没有额外())执行以下操作的方法:

ContactGroup.record_set.extra(where=["history_date = (select max(history_date) from app_record r where r.id=app_record.id and r.history_date <= '2009-07-18')"])

将子查询放在 where 子句中只是解决此问题的一种策略,我上面给出的第一个链接很好地涵盖了其他策略。我知道如果不使用 extra(),where-clause 子选择是不可能的,但我认为新的聚合功能可能使其他方法之一成为可能。

4

2 回答 2

0

听起来您想保留 Django 中对象更改的记录。

Pro Django在第 11 章(增强应用程序)中有一节介绍了如何创建一个模型,该模型使用另一个模型作为客户端,它跟踪插入/删除/更新。模型是从客户端定义动态生成的,并依赖于关于信号。该代码显示 most_recent() 函数,但您可以对其进行调整以获取特定日期的对象状态。

我认为这是有问题的 Django 中的跟踪,而不是获取此信息的 SQL,对吗?

于 2009-07-21T21:46:18.837 回答
0

首先,我要指出:

ContactGroup.record_set.extra(where=["history_date = (select max(history_date) from app_record r where r.id=app_record.id and r.history_date <= '2009-07-18')"])

不会得到与以下相同的效果:

records_i_want = group.record_set.most_recent_record_for_every_contact()

第一个查询返回与特定组相关联(或与特定组的任何联系人相关联)的每条记录,其 record_date 小于额外指定的日期/时间。在 shell 上运行它,然后执行此操作以查看 django 创建的查询:

from django.db import connection
connection.queries[-1]

这揭示了:

'SELECT "contacts_record"."id", "contacts_record"."contact_id", "contacts_record"."group_id", "contacts_record"."record_date", "contacts_record"."name", "contacts_record"."email" FROM "contacts_record" WHERE "contacts_record"."group_id" = 1  AND record_date = (select max(record_date) from contacts_record r where r.id=contacts_record.id and r.record_date <= \'2009-07-18\')

不完全是你想要的,对吧?

现在聚合功能用于检索聚合数据,而不是与聚合数据关联的对象。因此,如果您在尝试获取group.record_set.most_recent_record_for_every_contact()时尝试最小化使用聚合执行的查询数量,您将不会成功。

在不使用聚合的情况下,您可以使用以下方法获取与组关联的所有联系人的最新记录:

[x.record_set.all().order_by('-record_date')[0] for x in group.contact_set.all()]

使用聚合,我能得到的最接近的是:

group.record_set.values('contact').annotate(latest_date=Max('record_date'))

后者返回一个字典列表,如:

[{'contact': 1, 'latest_date': somedate }, {'contact': 2, 'latest_date': somedate }]

因此,给定组中的每个联系人都有一个条目,以及与之关联的最新记录日期。

无论如何,最小查询数可能是 1 + # 组中的联系人。如果您有兴趣使用单个查询获得结果,那也是可能的,但您必须以不同的方式构建模型。但这是你问题的一个完全不同的方面。

我希望这将帮助您了解如何使用聚合/常规 ORM 函数来解决问题。

于 2009-07-24T20:49:53.283 回答