Django-ORM:需要不同的。为什么?
Django-ORM: distinct is needed. Why?
我在玩 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)
我不明白第二个查询 which returns 1731.
我知道我可以使用 distinct()。
尽管如此,1731 对我来说似乎是一个错误。
以下查询不是distinct/unique的意图是什么?
User.objects.filter(groups__in=Group.objects.all())
原始 MySQL 查询如下所示:
SELECT user.id, group.id FROM user LEFT JOIN group ON user.group_id = group.id
结果将包含用户和组的所有可能组合,我猜有些用户属于多个组。
您正在尝试从所有组中获取所有用户,但一个用户可以出现在多个组中,这就是为什么需要 distinct 的原因。如果您希望特定组中的用户而不是执行 all 尝试 filter 查询。
我假设 User.groups
是一个 ForeignKey
或将每个 User
与零到多个 Group
实例相关联的其他关系。
所以让你困惑的查询:
User.objects.filter(groups__in=Group.objects.all())
该查询可以描述为:
- 访问
Group
模型管理器(Group.objects
)。
- 做一个
QuerySet
:
- Return 所有
Group
个实例(Group.objects.all()
)。
- 访问
User
模型管理器(User.objects
)。
- 做一个
Queryset
:
- 加入
Group
模型,在 User.groups
外键上。
- Return 每 (
User
+ Group
) 行有一个关联的 Group
.
那不是“一个组中的所有用户”;相反,它是“组存在的所有用户组对”。
通过查询 每个 多值 User.groups
字段,您暗示查询必须包含连接 从 User
到 Group
行。
相反,您想要:
- 访问
User
模型管理器(User.objects
)。
- 做一个
QuerySet
:
- Return 所有
groups
不为空的行。
User.objects.filter(groups__isnull=False)
请注意,这 - “所有具有一组非空关联组的用户” - 与您的另一个示例查询(“所有用户不在任何组中”)相反。
因为组是一个 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_groups
table。
其中 auth_user_groups
是 ManyToManyField
table 而不是 Group
模型的 table。这样一个用户就会不止一次来。
您可能希望使用 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
>>>
Annotate 在行为上类似于 distinct。它创建一个 group by
查询。您可以按如下方式查看和检查查询。
>>> print(User.objects.annotate(group_count=Count('groups')).filter(group_count__gt=0).query)
SELECT `auth_user`.`id`, `auth_user`.`password`, `auth_user`.`last_login`, `auth_user`.`is_superuser`, `auth_user`.`username`, `auth_user`.`first_name`, `auth_user`.`last_name`, `auth_user`.`email`, `auth_user`.`is_staff`, `auth_user`.`is_active`, `auth_user`.`date_joined`, COUNT(`auth_user_groups`.`group_id`) AS `group_count` FROM `auth_user` LEFT OUTER JOIN `auth_user_groups` ON (`auth_user`.`id` = `auth_user_groups`.`user_id`) GROUP BY `auth_user`.`id` HAVING COUNT(`auth_user_groups`.`group_id`) > 0 ORDER BY NULL
当您 运行 对数据库进行 'DISTINCT' 查询时,您最终会得到数据结果中每个不同行的列表。 Django 结果中有更多 'DISTINCT' 行的原因是正在进行组合交叉乘法,从而产生额外的结果。
其他答案都提到了所有这些,但既然你问的是为什么:
在此联接中,ORM 可能允许您从查询中提取附加到组的字段。所以如果你想要,比如说,所有这些用户和所有组以及组联系人进行某种大量奇怪的邮件合并,你可以得到它们。
DISTINCT 带来的 post 处理根据您拉取的 字段 而不是查询中的行来缩小结果范围。如果您要使用 PyCharm 调试器或其他工具,您可能会发现使用不同的 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)
我不明白第二个查询 which returns 1731.
我知道我可以使用 distinct()。
尽管如此,1731 对我来说似乎是一个错误。
以下查询不是distinct/unique的意图是什么?
User.objects.filter(groups__in=Group.objects.all())
原始 MySQL 查询如下所示:
SELECT user.id, group.id FROM user LEFT JOIN group ON user.group_id = group.id
结果将包含用户和组的所有可能组合,我猜有些用户属于多个组。
您正在尝试从所有组中获取所有用户,但一个用户可以出现在多个组中,这就是为什么需要 distinct 的原因。如果您希望特定组中的用户而不是执行 all 尝试 filter 查询。
我假设 User.groups
是一个 ForeignKey
或将每个 User
与零到多个 Group
实例相关联的其他关系。
所以让你困惑的查询:
User.objects.filter(groups__in=Group.objects.all())
该查询可以描述为:
- 访问
Group
模型管理器(Group.objects
)。 - 做一个
QuerySet
:- Return 所有
Group
个实例(Group.objects.all()
)。
- Return 所有
- 访问
User
模型管理器(User.objects
)。 - 做一个
Queryset
:- 加入
Group
模型,在User.groups
外键上。 - Return 每 (
User
+Group
) 行有一个关联的Group
.
- 加入
那不是“一个组中的所有用户”;相反,它是“组存在的所有用户组对”。
通过查询 每个 多值 User.groups
字段,您暗示查询必须包含连接 从 User
到 Group
行。
相反,您想要:
- 访问
User
模型管理器(User.objects
)。 - 做一个
QuerySet
:- Return 所有
groups
不为空的行。
- Return 所有
User.objects.filter(groups__isnull=False)
请注意,这 - “所有具有一组非空关联组的用户” - 与您的另一个示例查询(“所有用户不在任何组中”)相反。
因为组是一个 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_groups
table。
其中 auth_user_groups
是 ManyToManyField
table 而不是 Group
模型的 table。这样一个用户就会不止一次来。
您可能希望使用 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
>>>
Annotate 在行为上类似于 distinct。它创建一个 group by
查询。您可以按如下方式查看和检查查询。
>>> print(User.objects.annotate(group_count=Count('groups')).filter(group_count__gt=0).query)
SELECT `auth_user`.`id`, `auth_user`.`password`, `auth_user`.`last_login`, `auth_user`.`is_superuser`, `auth_user`.`username`, `auth_user`.`first_name`, `auth_user`.`last_name`, `auth_user`.`email`, `auth_user`.`is_staff`, `auth_user`.`is_active`, `auth_user`.`date_joined`, COUNT(`auth_user_groups`.`group_id`) AS `group_count` FROM `auth_user` LEFT OUTER JOIN `auth_user_groups` ON (`auth_user`.`id` = `auth_user_groups`.`user_id`) GROUP BY `auth_user`.`id` HAVING COUNT(`auth_user_groups`.`group_id`) > 0 ORDER BY NULL
当您 运行 对数据库进行 'DISTINCT' 查询时,您最终会得到数据结果中每个不同行的列表。 Django 结果中有更多 'DISTINCT' 行的原因是正在进行组合交叉乘法,从而产生额外的结果。
其他答案都提到了所有这些,但既然你问的是为什么: 在此联接中,ORM 可能允许您从查询中提取附加到组的字段。所以如果你想要,比如说,所有这些用户和所有组以及组联系人进行某种大量奇怪的邮件合并,你可以得到它们。
DISTINCT 带来的 post 处理根据您拉取的 字段 而不是查询中的行来缩小结果范围。如果您要使用 PyCharm 调试器或其他工具,您可能会发现使用不同的 ORM 语法访问组不像没有时那样容易。