Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions authentik/core/api/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,15 @@ def _should_include_children(self) -> bool:
def get_users_obj(self, instance: Group) -> list[PartialUserSerializer] | None:
if not self._should_include_users:
return None
return PartialUserSerializer(instance.users, many=True).data
# Use .all() to access prefetched data instead of triggering new queries
return PartialUserSerializer(instance.users.all(), many=True).data

@extend_schema_field(GroupChildSerializer(many=True))
def get_children_obj(self, instance: Group) -> list[GroupChildSerializer] | None:
if not self._should_include_children:
return None
return GroupChildSerializer(instance.children, many=True).data
# Use .all() to access prefetched data instead of triggering new queries
return GroupChildSerializer(instance.children.all(), many=True).data

def validate_parent(self, parent: Group | None):
"""Validate group parent (if set), ensuring the parent isn't itself"""
Expand Down Expand Up @@ -251,8 +253,18 @@ def get_queryset(self):
Prefetch("users", queryset=User.objects.all().only("id"))
)

# Always prefetch children since the serializer includes 'children' field for PKs
# even when include_children=false
if self.serializer_class(context={"request": self.request})._should_include_children:
base_qs = base_qs.prefetch_related("children")
# When including full children data, prefetch with parent selected
base_qs = base_qs.prefetch_related(
Prefetch("children", queryset=Group.objects.select_related("parent"))
)
else:
# When only needing PKs, just prefetch IDs
base_qs = base_qs.prefetch_related(
Prefetch("children", queryset=Group.objects.all().only("group_uuid"))
)

return base_qs

Expand Down
22 changes: 20 additions & 2 deletions authentik/core/api/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from django.contrib.auth import update_session_auth_hash
from django.contrib.auth.models import AnonymousUser, Permission
from django.db.models import Exists, OuterRef, Prefetch
from django.db.transaction import atomic
from django.db.utils import IntegrityError
from django.urls import reverse_lazy
Expand Down Expand Up @@ -147,7 +148,8 @@ def _should_include_groups(self) -> bool:
def get_groups_obj(self, instance: User) -> list[PartialGroupSerializer] | None:
if not self._should_include_groups:
return None
return PartialGroupSerializer(instance.ak_groups, many=True).data
# Use .all() to access prefetched data instead of triggering new queries
return PartialGroupSerializer(instance.ak_groups.all(), many=True).data

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Expand Down Expand Up @@ -453,8 +455,24 @@ def get_ql_fields(self):

def get_queryset(self):
base_qs = User.objects.all().exclude_anonymous()

# Always prefetch groups since UserSerializer includes 'groups' field for PKs
if self.serializer_class(context={"request": self.request})._should_include_groups:
base_qs = base_qs.prefetch_related("ak_groups")
# When including full group data, prefetch with parent selected
base_qs = base_qs.prefetch_related(
Prefetch(
"ak_groups",
queryset=Group.objects.select_related("parent"),
)
)
else:
# When only needing PKs, just prefetch minimal data
base_qs = base_qs.prefetch_related(
Prefetch(
"ak_groups",
queryset=Group.objects.only("pk"),
)
)
return base_qs

@extend_schema(
Expand Down
Loading