Django - 找到每个组的极端成员

时间:2009-07-21 21:21:46

标签: sql django model aggregate

我一直在使用Django ORM中的新聚合功能,并且我认为应该是可能的一类问题,但我似乎无法让它工作。我正在尝试生成的查询类型为here

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

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关联的每个联系人的最新Record实例。在伪代码中:

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子句子选择是不可能的,但我想也许新的聚合功能可能使其他方法成为可能。

2 个答案:

答案 0 :(得分:0)

听起来你想在Django中记录对象的变化。

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

我认为Django中的跟踪存在问题,而不是获取此问题的SQL,对吗?

答案 1 :(得分: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()

第一个查询返回与特定组相关联的每个记录(或与特定组的任何联系人关联),其记录的记录小于额外中指定的日期/时间。在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函数来解决问题。