Skip to content

Commit

Permalink
Update get_users_with_perms to avoid a large join
Browse files Browse the repository at this point in the history
This code was creating a large number of table joins
"Users that are part of a group that have perms to obj" requires that we join 5 tables: user - m2m - group - m2m - obj

Instead; we perform an intermediate query to fetch all of the groups that have a perm record to the object (a join across 3 tables), and then we fetch all of the users that belong to one of those groups (a join across 3 tables).
  • Loading branch information
aaronmader authored and michael-k committed Jan 16, 2020
1 parent cea980c commit b5de761
Showing 1 changed file with 10 additions and 10 deletions.
20 changes: 10 additions & 10 deletions guardian/shortcuts.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def assign_perm(perm, user_or_group, obj=None):
``Permission`` instance.
:param user_or_group: instance of ``User``, ``AnonymousUser``, ``Group``,
list of ``User`` or ``Group``, or queryset of ``User`` or ``Group``;
list of ``User`` or ``Group``, or queryset of ``User`` or ``Group``;
passing any other object would raise
``guardian.exceptions.NotUserNorGroup`` exception
Expand Down Expand Up @@ -294,21 +294,21 @@ def get_users_with_perms(obj, attach_perms=False, with_superusers=False,
})
if with_group_users:
group_model = get_group_obj_perms_model(obj)
group_rel_name = group_model.group.field.related_query_name()
if group_model.objects.is_generic():
group_filters = {
'groups__%s__content_type' % group_rel_name: ctype,
'groups__%s__object_pk' % group_rel_name: obj.pk,
group_obj_perm_filters = {
'content_type': ctype,
'object_pk': obj.pk,
}
else:
group_filters = {
'groups__%s__content_object' % group_rel_name: obj,
group_obj_perm_filters = {
'content_object': obj,
}
if only_with_perms_in is not None:
group_filters.update({
'groups__%s__permission_id__in' % group_rel_name: permission_ids,
group_obj_perm_filters.update({
'permission_id__in': permission_ids,
})
qset = qset | Q(**group_filters)
group_ids = set(group_model.objects.filter(**group_obj_perm_filters).values_list('group_id', flat=True).distinct())
qset = qset | Q(groups__in=group_ids)
if with_superusers:
qset = qset | Q(is_superuser=True)
return get_user_model().objects.filter(qset).distinct()
Expand Down

0 comments on commit b5de761

Please sign in to comment.