【问题标题】:Weird behavior of Django ORMDjango ORM 的奇怪行为
【发布时间】:2014-09-27 00:05:52
【问题描述】:

我正在使用 Django ORM 执行一个看起来像这样的复杂查询:

user_ids = MyLog.objects.values('user_id').annotate(
    last_configured=Max('configured')
).exclude(
    last_configured__lt=earlier_date
).filter(content_type_id=configuration_content_type).values_list('user_id', flat=True)

计算结果为:

SELECT `customer_mylog`.`user_id`, MAX(`customer_mylog`.`configured`)
AS `last_configured` FROM `customer_mylog`
WHERE (`customer_mylog`.`content_type_id` = 654 )
GROUP BY `customer_mylog`.`user_id`
HAVING NOT (MAX(`customer_mylog`.`configured`) < 2014-04-19 20:22:38.729416 )
ORDER BY NULL

此时,我确信查询尚未执行,除非我执行类似len(user_ids) 之类的操作,而我没有这样做。然后我通过执行以下操作对其进行修改:

new_user_ids = User.objects.filter(pk__in=user_ids).exclude(date_joined__lt=earlier_date).exists()

将原始查询更改为:

SELECT `auth_user`.`id`, `auth_user`.`username`, `auth_user`.`first_name`, `auth_user`.`la 
st_name`, `auth_user`.`email`, `auth_user`.`password`, `auth_user`.`is_staff`, `auth_user`.` 
is_active`, `auth_user`.`is_superuser`, `auth_user`.`last_login`, `auth_user`.`date_joined`  
FROM `auth_user` WHERE (`auth_user`.`id` IN (SELECT U0.`user_id` FROM `customer_mylo 
g` U0 WHERE (U0.`content_type_id` = 654 ) GROUP BY U0.`user_id` HAVING NOT (MAX(U0.`configured`)  
< 2014-05-04 13:04:48.204187 ) ORDER BY NULL) AND NOT (`auth_user`.`date_joined` < 2014-05-0 
4 13:04:48 ))

如果我通过执行 list(new_user_ids) 之类的操作来执行查询,它永远不会完成执行。它既不会失败,也不会给出任何错误消息。

因此,我尝试了以下方法,它以某种方式完美地工作。

for user_id in user_ids:
    if User.objects.filter(pk=user_id).exclude(date_joined__lt=earlier_date).exists():
        new_user_ids.append(user_id)

为什么前一种方法不起作用,尽管两者在尝试做的事情上基本相似?

【问题讨论】:

  • 除非您要求它,否则它永远不会评估:docs.djangoproject.com/en/1.6/ref/models/querysets/…
  • @petkostas 我想我无法在问题中正确解释自己。请再次查看。
  • 如果你尝试通过len来评估它?你得到了什么?如 django 文档中所述(关于列表):Be warned, though, that this could have a large memory overhead, because Django will load each element of the list into memory.
  • 另外尝试在 shell 中运行查询集并检查会发生什么。
  • 您使用哪个数据库和数据库版本?旧的 MySQL 版本确实没有针对复杂的子查询进行很好的优化。

标签: python mysql django orm django-queryset


【解决方案1】:

Django Querysets 不直接评估,为了让 ORM 评估它们并查询你需要对它们执行实际操作的底层数据库:

https://docs.djangoproject.com/en/1.6/ref/models/querysets/#when-querysets-are-evaluated

【讨论】:

    【解决方案2】:

    不同之处在于您在一个查询集上调用 .exists(),在数据库中完成所有繁重的工作并返回一个布尔值,而另一个您是 filtering 并将所有内容加载到内存中Python 对象。

    您的最后一段代码,exists() 检查实际上并没有实例化任何对象,它只在数据库中检查用户是否匹配查询并停止,而不是生成所有用户对象的列表

    实例化所有模型需要更多的时间,而不是仅仅计算是否可以实例化它们。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-01-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-06
      • 2016-04-27
      • 2018-09-09
      • 1970-01-01
      相关资源
      最近更新 更多