进行子选择的django方法是什么?

时间:2016-05-18 14:45:01

标签: python django django-models

我有一个版本化的模型(简化)看起来有点像:

Project(id, ref, version)

unique_together(ref, version)

其中id是自动生成的主键,ref是随机UUID,version是由我的应用程序递增的整数。每次保存项目时,我都会创建一个新实例,在版本中添加1并将ref复制到新对象。

以下SQL将通过执行subselect返回每个Project的最新版本。

SELECT * FROM myapp_project WHERE (ref, version) IN
(SELECT ref, max(version) FROM myapp_project GROUP BY ref)

或者(或许稍微简单):

SELECT * from myapp_project p
WHERE p.version =
(SELECT max(version) FROM myapp_project p1 WHERE p1.ref = p.ref)

如何使用Django的ORM实现相同的查询?

编辑:我已经达到了这个目标 -

foo = Project.objects.values('ref').annotate(version=Max('version'))

如果我检查它,这给了我一些看起来正确的东西。一旦我尝试将id输出:

foo.values('id')

似乎丢弃原始结果并返回所有行。

修改更多内容:

现在用.extra():

解决这个问题
maxids = """id in (SELECT id from myapp_project p WHERE p.version = 
(SELECT max(version) FROM myapp_project p1 WHERE p1.ref = p.ref))"""

Project.objects.all().extra(where=[maxids])

3 个答案:

答案 0 :(得分:2)

使用in

以下是直接来自链接文档的示例

inner_qs = Blog.objects.filter(name__contains='Cheddar')
entries = Entry.objects.filter(blog__in=inner_qs)

您正在使用的确切查询不是所有使用Django的数据库都支持的查询。例如,虽然它适用于postgresql但它不适用于sqlite,因此您必须更改使用主键而不是unique_together键的方法。或者使用联接。

Update第二个查询可以更轻松地处理,但我不会在此处发布,因为这似乎是对@anand的答案进行诽谤。

答案 1 :(得分:1)

您可以像这样构建它:

from django.db.models import Max
latest_refs_with_max_id = Project.objects.values('ref').annotate(Max('version'), Max('id')).values('id__max')

latest_refs = [ d['id__max'] for d in latest_refs_with_max_id]

q = Project.objects.filter(id__in=latest_refs)

Testing

注意:如果您有多条记录具有相同的最大versionref

,则会选择最大ID

答案 2 :(得分:1)

您可以执行以下操作:首先获取所有引用,然后为每个引用获得具有最高版本的项目:

projects = []
for ref in Project.objects.all().values_list('ref', flat=True).distinct():
    projects.append(Project.objects.filter(ref=ref).order_by('-version')[0])

更高效的版本:

from django.db.models import Max
max_project_versions = Project.objects.values('ref').annotate(id_max=Max('version')).values_list('id_max', flat=True)
projects = Project.objects.filter(id__in=max_project_versions)