Django-ORM:需要与众不同。为什么?

时间:2018-06-22 15:06:37

标签: django django-orm

我正在和Django ORM玩耍

import django
django.setup()
from django.contrib.auth.models import User, Group
from django.db.models import Count

# All users
print(User.objects.all().count())
# --> 742

# Should be: All users which are in a group.
# But the result is different. I don't understand this.
print(User.objects.filter(groups__in=Group.objects.all()).count())
# --> 1731

# All users which are in a group.
# distinct needed
print(User.objects.filter(groups__in=Group.objects.all()).distinct().count())
# --> 543

# All users which are in a group. Without distinct, annotate seems to do this.
print(User.objects.filter(groups__in=Group.objects.all()).annotate(Count('pk')).count())
# --> 543

# All users which are in no group
print(User.objects.filter(groups__isnull=True).count())
# --> 199

# 199 + 543 = 742  (nice)

我不明白返回1731的第二个查询。

我知道我可以使用distinct()。

尽管如此,1731在我看来还是个虫子。

下面的查询为何不是唯一/独特的目的是什么?

User.objects.filter(groups__in=Group.objects.all())

5 个答案:

答案 0 :(得分:6)

原始MySQL查询如下:

SELECT user.id, group.id FROM user LEFT JOIN group ON user.group_id = group.id

结果将包含用户和组的所有可能组合,我想有些用户属于多个组。

答案 1 :(得分:5)

您正试图从所有组中获取所有用户,但是一个用户可以在多个组中显示,这就是为什么需要与众不同的原因。如果您希望用户位于特定的组中,而不是执行 all ,请尝试执行过滤器查询。

答案 2 :(得分:3)

我假设User.groupsForeignKey或某种其他关系,将每个User与零到许多Group实例相关联。

因此使您感到困惑的查询:

User.objects.filter(groups__in=Group.objects.all())

该查询可以描述为:

  • 访问Group模型管理器(Group.objects)。
  • 制作一个QuerySet
    • 返回所有Group个实例(Group.objects.all())。
  • 访问User模型管理器(User.objects)。
  • 制作一个Queryset
    • 使用Group外键加入User.groups模型。
    • 返回每行(User + Group)中具有关联的Group的行。

不是“一个组中的所有用户”;而是“组所在的所有用户组对”。

通过查询多值User.groups字段的中的每一个,您暗示查询必须包含从UserGroup行。


您要:

  • 访问User模型管理器(User.objects)。
  • 制作一个QuerySet
    • 返回所有groups不为空的行。
User.objects.filter(groups__isnull=False)

请注意,这是“所有具有一组非空关联组的用户”,与您拥有的另一个示例查询(“所有不属于组的用户”)相反。

答案 3 :(得分:2)

由于组是一个SecureString查询,翻译为ManyToManyField语句。

如果打印以下内容,您将看到INNER JOIN生成的查询:

QuerySet

您将看到查询联接了>>> print(User.objects.filter(groups__in=Group.objects.all()).query) SELECT `auth_user`.`id`, .... , `auth_user`.`date_joined` FROM `auth_user` INNER JOIN `auth_user_groups` ON (`auth_user`.`id` = `auth_user_groups`.`user_id`) WHERE `auth_user_groups`.`group_id` IN (SELECT `auth_group`.`id` FROM `auth_group`) auth_user表。 其中auth_user_groupsauth_user_groups表,而不是ManyToManyField模型的表。因此,用户将不止一次来。

您可能希望使用Group吸引用户,在我的情况下,数字如下:

annotate

注释与行为相似。它创建一个$ ./manage.py shell >>> >>> from django.contrib.auth.models import User, Group >>> from django.db.models import Count >>> # All users >>> print(User.objects.all().count()) 556 >>> # All users which are not in a group. >>> print(User.objects.annotate(group_count=Count('groups')).filter(group_count=0).count()) 44 >>> # All users which are in a group. >>> print(User.objects.annotate(group_count=Count('groups')).filter(group_count__gt=0).count()) 512 >>> 查询。您可以按照以下方式查看和检查查询。

group by

答案 4 :(得分:0)

对数据库运行“ DISTINCT”查询时,最终会列出数据结果中每个不同的行。在Django结果中有更多“ DISTINCT”行的原因是组合交叉乘法不断进行,从而产生了额外的结果。

其他答案都提到了所有这些,但是由于您要问为什么: 在此联接中,ORM可能会允许您从查询中提取附加到组的字段。因此,例如,如果您希望所有这些用户,所有组以及组联系人进行某种大规模的怪异邮件合并,则可以获取它们。

DISTINCT带来的后期处理将根据您提取的字段而不是查询中的行来缩小结果范围。如果您要使用PyCharm调试器或其他工具,则可能会发现使用不同的ORM语法访问组时不如不使用时容易。