diff --git a/awx/api/views/__init__.py b/awx/api/views/__init__.py index 93cb5b0920..e8754af8bf 100644 --- a/awx/api/views/__init__.py +++ b/awx/api/views/__init__.py @@ -61,7 +61,7 @@ from wsgiref.util import FileWrapper # AWX from awx.main.tasks import send_notifications, update_inventory_computed_fields -from awx.main.access import get_user_queryset +from awx.main.access import get_user_queryset, HostAccess from awx.api.filters import V1CredentialFilterBackend from awx.api.generics import ( APIView, BaseUsersList, CopyAPIView, DeleteLastUnattachLabelMixin, @@ -670,18 +670,6 @@ class ProjectList(ListCreateAPIView): model = models.Project serializer_class = serializers.ProjectSerializer - def get_queryset(self): - projects_qs = models.Project.accessible_objects(self.request.user, 'read_role') - projects_qs = projects_qs.select_related( - 'organization', - 'admin_role', - 'use_role', - 'update_role', - 'read_role', - ) - projects_qs = projects_qs.prefetch_related('last_job', 'created_by') - return projects_qs - class ProjectDetail(RelatedJobsPreventDeleteMixin, RetrieveUpdateDestroyAPIView): @@ -1539,7 +1527,10 @@ class InventoryHostsList(HostRelatedSearchMixin, SubListCreateAttachDetachAPIVie def get_queryset(self): inventory = self.get_parent_object() - return getattrd(inventory, self.relationship).all() + qs = getattrd(inventory, self.relationship).all() + # Apply queryset optimizations + qs = qs.select_related(*HostAccess.select_related).prefetch_related(*HostAccess.prefetch_related) + return qs class HostGroupsList(ControlledByScmMixin, SubListCreateAttachDetachAPIView): @@ -4427,18 +4418,6 @@ class RoleList(ListAPIView): permission_classes = (IsAuthenticated,) search_fields = ('role_field', 'content_type__model',) - def get_queryset(self): - result = models.Role.visible_roles(self.request.user) - # Sanity check: is the requesting user an orphaned non-admin/auditor? - # if yes, make system admin/auditor mandatorily visible. - if not self.request.user.organizations.exists() and\ - not self.request.user.is_superuser and\ - not self.request.user.is_system_auditor: - mandatories = ('system_administrator', 'system_auditor') - super_qs = models.Role.objects.filter(singleton_name__in=mandatories) - result = result | super_qs - return result - class RoleDetail(RetrieveAPIView): diff --git a/awx/api/views/inventory.py b/awx/api/views/inventory.py index c64521202a..a057c7cdf2 100644 --- a/awx/api/views/inventory.py +++ b/awx/api/views/inventory.py @@ -115,12 +115,6 @@ class InventoryList(ListCreateAPIView): model = Inventory serializer_class = InventorySerializer - def get_queryset(self): - qs = Inventory.accessible_objects(self.request.user, 'read_role') - qs = qs.select_related('admin_role', 'read_role', 'update_role', 'use_role', 'adhoc_role') - qs = qs.prefetch_related('created_by', 'modified_by', 'organization') - return qs - class InventoryDetail(RelatedJobsPreventDeleteMixin, ControlledByScmMixin, RetrieveUpdateDestroyAPIView): diff --git a/awx/api/views/root.py b/awx/api/views/root.py index c6c9d65dae..0456631ce8 100644 --- a/awx/api/views/root.py +++ b/awx/api/views/root.py @@ -167,7 +167,7 @@ class ApiV1PingView(APIView): capacity=instance.capacity, version=instance.version)) sorted(response['instances'], key=operator.itemgetter('node')) response['instance_groups'] = [] - for instance_group in InstanceGroup.objects.all(): + for instance_group in InstanceGroup.objects.prefetch_related('instances'): response['instance_groups'].append(dict(name=instance_group.name, capacity=instance_group.capacity, instances=[x.hostname for x in instance_group.instances.all()])) diff --git a/awx/main/access.py b/awx/main/access.py index 12de86b73f..1b3753c412 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -687,6 +687,7 @@ class OAuth2ApplicationAccess(BaseAccess): model = OAuth2Application select_related = ('user',) + prefetch_related = ('organization', 'oauth2accesstoken_set') def filtered_queryset(self): org_access_qs = Organization.accessible_objects(self.user, 'member_role') @@ -725,6 +726,7 @@ class OAuth2TokenAccess(BaseAccess): model = OAuth2AccessToken select_related = ('user', 'application') + prefetch_related = ('refresh_token',) def filtered_queryset(self): org_access_qs = Organization.objects.filter( @@ -826,7 +828,7 @@ class InventoryAccess(BaseAccess): ''' model = Inventory - select_related = ('created_by', 'modified_by', 'organization',) + prefetch_related = ('created_by', 'modified_by', 'organization') def filtered_queryset(self, allowed=None, ad_hoc=None): return self.model.accessible_objects(self.user, 'read_role') @@ -1090,8 +1092,8 @@ class InventoryUpdateAccess(BaseAccess): ''' model = InventoryUpdate - select_related = ('created_by', 'modified_by', 'inventory_source__inventory',) - prefetch_related = ('unified_job_template', 'instance_group', 'credentials',) + select_related = ('created_by', 'modified_by', 'inventory_source',) + prefetch_related = ('unified_job_template', 'instance_group', 'credentials__credential_type', 'inventory', 'source_script') def filtered_queryset(self): return self.model.objects.filter(inventory_source__inventory__in=Inventory.accessible_pk_qs(self.user, 'read_role')) @@ -1105,11 +1107,7 @@ class InventoryUpdateAccess(BaseAccess): return self.user in obj.inventory_source.inventory.admin_role def can_start(self, obj, validate_license=True): - # For relaunching - if obj and obj.inventory_source: - access = InventorySourceAccess(self.user) - return access.can_start(obj.inventory_source, validate_license=validate_license) - return False + return InventorySourceAccess(self.user).can_start(obj, validate_license=validate_license) @check_superuser def can_delete(self, obj): @@ -1127,6 +1125,7 @@ class CredentialTypeAccess(BaseAccess): ''' model = CredentialType + prefetch_related = ('created_by', 'modified_by',) def can_read(self, obj): return True @@ -1363,7 +1362,8 @@ class ProjectAccess(NotificationAttachMixin, BaseAccess): ''' model = Project - select_related = ('modified_by', 'credential', 'current_job', 'last_job',) + select_related = ('credential',) + prefetch_related = ('modified_by', 'created_by', 'organization', 'last_job', 'current_job') notification_attach_roles = ['admin_role'] def filtered_queryset(self): @@ -1856,7 +1856,7 @@ class WorkflowJobTemplateNodeAccess(BaseAccess): ''' model = WorkflowJobTemplateNode prefetch_related = ('success_nodes', 'failure_nodes', 'always_nodes', - 'unified_job_template', 'credentials',) + 'unified_job_template', 'credentials', 'workflow_job_template') def filtered_queryset(self): return self.model.objects.filter( @@ -1948,9 +1948,8 @@ class WorkflowJobNodeAccess(BaseAccess): Deletion must happen as a cascade delete from the workflow job. ''' model = WorkflowJobNode - select_related = ('unified_job_template', 'job',) - prefetch_related = ('success_nodes', 'failure_nodes', 'always_nodes', - 'credentials',) + prefetch_related = ('unified_job_template', 'job', 'workflow_job', 'credentials', + 'success_nodes', 'failure_nodes', 'always_nodes',) def filtered_queryset(self): return self.model.objects.filter( @@ -2497,6 +2496,7 @@ class NotificationTemplateAccess(BaseAccess): I can see/use a notification_template if I have permission to ''' model = NotificationTemplate + prefetch_related = ('created_by', 'modified_by', 'organization') def filtered_queryset(self): return self.model.objects.filter( @@ -2683,6 +2683,7 @@ class ActivityStreamAccess(BaseAccess): class CustomInventoryScriptAccess(BaseAccess): model = CustomInventoryScript + prefetch_related = ('created_by', 'modified_by', 'organization') def filtered_queryset(self): return self.model.accessible_objects(self.user, 'read_role').all() @@ -2716,6 +2717,17 @@ class RoleAccess(BaseAccess): ''' model = Role + prefetch_related = ('content_type',) + + def filtered_queryset(self): + result = Role.visible_roles(self.user) + # Sanity check: is the requesting user an orphaned non-admin/auditor? + # if yes, make system admin/auditor mandatorily visible. + if not self.user.is_superuser and not self.user.is_system_auditor and not self.user.organizations.exists(): + mandatories = ('system_administrator', 'system_auditor') + super_qs = Role.objects.filter(singleton_name__in=mandatories) + result = result | super_qs + return result def can_read(self, obj): if not obj: