我正在做一些原型制作,并有一个像这样的简单模型
class SampleModel(models.Model):
user_id = models.IntegerField(default=0, db_index=True)
staff_id = models.IntegerField(default=0, db_index=True)
timestamp = models.DateTimeField(default=timezone.now, db_index=True)
objects = AsOfManager()
现在,我们需要执行需要自我连接的查询,用原始SQL编写的查询就是这样的:
SELECT X.* FROM no_chain_samplemodel as X
JOIN (SELECT user_id, MAX(timestamp) AS timestamp
FROM no_chain_samplemodel
GROUP BY user_id) AS Y
ON (X.user_id = Y.user_id and X.timestamp = Y.timestamp);
此查询应为每个user_id返回时间戳的最后一行顺序。(与user_id相关的行)每个“链”都可能具有数千行。
现在,我可以使用原始SQL,但是随后我失去了可组合性,我想返回另一个查询集。同时,这也很容易使原始SQL编写变得容易,所以我认为我可以使用数据库视图。
视图可能就是这样
CREATE VIEW no_chain_sample_model_with_max_date AS SELECT user_id AS id, MAX(timestamp) AS timestamp
FROM no_chain_samplemodel
GROUP BY user_id;
因此,引用视图的模型可以像这样:
class SampleModelWithMaxDate(models.Model):
class Meta:
managed = False
db_table = 'no_chain_sample_model_with_max_date'
id = models.IntegerField(default=0, primary_key=True)
timestamp = models.DateTimeField(default=timezone.now, db_index=True)
但是,存在一些问题:
即使managed = False'./manage.py makemigrations'仍会为此表创建迁移。我什至试图将迁移留在那儿,但是用原始SQL替换模型以创建视图,但是没有运气。
我现在需要执行select_related来联接两个表和查询,但是我应该怎么做呢?
我在SampleModel上尝试了一个外键,如下所示:
by_date = models.ForeignKey(SampleModelWithMaxDate,null = True)
但这也不起作用:
OperationalError:(1054,““字段列表”中的未知列'no_chain_sample_model_with_max_date.by_date_id'”)
因此,总的来说,我什至不确定是否可行,我可以看到其他人正在使用带有视图的模型,并且只是查询也对我有用的独立模型,但是有什么办法比这更聪明吗?
谢谢
解决方案
我找不到任何ORM方法来在一个查询中获得所需的内容,但是我们可以通过两个查询来做到这一点:
首先,我们timestamp为所有用户获得最大收益
latest_timestamps = SampleModel.objects.values('user_id')
.annotate(max_ts=Max('timestamp')).values('max_ts')
在这里values(user_id)作为group by操作。
现在,我们获得了SampleModel具有确切时间戳的所有实例
qs = SampleModel.objects.filter(timestamp__in=latest_timestamps)
PostgreSQL特殊答案:
SampleModel.objects.order_by('user_id', '-timestamp').distinct('user_id')
分解:
# order by user_id, and in decreasing order of timestamp
qs = SampleModel.objects.order_by('user_id', '-timestamp')
# get distinct rows using user_id, this will make sure that the first entry for
# each user is retained and since we further ordered in decreasing order of
# timestamp for each user the first entry will have last row added
# for the user in the database.
qs = qs.distinct('user_id')