我需要一些帮助将这个查询放在Django中。我已经简化了这里的例子,直到切入点。
MyModel(models.Model):
created = models.DateTimeField()
user = models.ForeignKey(User)
data = models.BooleanField()
我想用英语创建的查询听起来像是:
向我提供昨天创建的数据为False的每条记录,其中相同范围的数据对于给定用户来说永远不会显示为True
这是一个示例输入/输出,如果不清楚的话。
表值
ID Created User Data
1 1/1/2010 admin False
2 1/1/2010 joe True
3 1/1/2010 admin False
4 1/1/2010 joe False
5 1/2/2010 joe False
输出查询集
1 1/1/2010 admin False
3 1/1/2010 admin False
我要做的是排除第4号记录。原因是因为在给定范围“昨天”中,对于记录#2中的用户,数据显示为True一次,因此将排除记录#4。
从某种意义上说,似乎有2个查询正在进行中。一个用于确定给定范围内的记录,另一个用于排除与“True”记录相交的记录。
如何使用Django ORM进行此查询?
答案 0 :(得分:5)
您不需要嵌套查询。您可以生成坏用户PK的列表,然后在下一个查询中排除包含这些PK的记录。
bad = list(set(MyModel.obejcts.filter(data=True).values_list('user', flat=True)))
# list(set(list_object)) will remove duplicates
# not needed but might save the DB some work
rs = MyModel.objects.filter(datequery).exclude(user__pk__in=bad)
# might not need the pk in user__pk__in - try it
你可以把它压缩成一行,但我认为这样你就会很整洁。 2个查询并不是那么糟糕。
编辑:您可能想阅读有关此文档的文档:
http://docs.djangoproject.com/en/dev/ref/models/querysets/#in
它听起来像是自动嵌套查询(因此只有一个查询在数据库中触发),如果它是这样的:
bad = MyModel.objects.filter(data=True).values('pk')
rs = MyModel.objects.filter(datequery).exclude(user__pk__in=bad)
但是 MySQL没有很好地优化这一点,所以我上面的代码(2个完整查询)实际上可以更快地运行。
尝试两种方式并参加比赛!
答案 1 :(得分:0)
看起来你可以使用:
来自django.db.models导入F.
MyModel.objects.filter(datequery).filter(data=False).filter(data = F('data'))
F
中提供的 1.0
对象
请测试一下,我不确定。
答案 2 :(得分:0)
由于延迟评估,您可以将查询分解为几个不同的变量,以便于阅读。以下是Oli已经呈现的风格的./manage.py shell
播放时间。
> from django.db import connection
> connection.queries = []
> target_day_qs = MyModel.objects.filter(created='2010-1-1')
> bad_users = target_day_qs.filter(data=True).values('user')
> result = target_day_qs.exclude(user__in=bad_users)
> [r.id for r in result]
[1, 3]
> len(connection.queries)
1
如果您想在同一查询中提取用户对象,也可以说result.select_related()
。