perf: 修改 labels 和 role 搜索

pull/12363/head
ibuler 2023-12-18 17:56:16 +08:00 committed by Bryan
parent 1e7f268f0c
commit 1358cf532f
9 changed files with 76 additions and 39 deletions

View File

@ -59,7 +59,10 @@ class EncryptedField(serializers.CharField):
class LabeledChoiceField(ChoiceField):
def __init__(self, *args, **kwargs):
super(LabeledChoiceField, self).__init__(*args, **kwargs)
self.choice_mapper = {
@property
def choice_mapper(self):
return {
key: value for key, value in self.choices.items()
}

View File

@ -9,9 +9,10 @@ from orgs.utils import current_org
from rbac.models import ContentType
from rbac.serializers import ContentTypeSerializer
from . import serializers
from .const import label_resource_types
from .models import Label, LabeledResource
__all__ = ['LabelViewSet']
__all__ = ['LabelViewSet', 'ContentTypeViewSet']
class ContentTypeViewSet(JMSModelViewSet):
@ -25,26 +26,8 @@ class ContentTypeViewSet(JMSModelViewSet):
can_labeled_content_type = []
model = ContentType
@classmethod
def get_can_labeled_content_type_ids(cls):
if cls.can_labeled_content_type:
return cls.can_labeled_content_type
content_types = ContentType.objects.all()
for ct in content_types:
model_cls = ct.model_class()
if not model_cls:
continue
if model_cls._meta.parents:
continue
if 'labels' in model_cls._meta._forward_fields_map.keys():
# if issubclass(model_cls, LabeledMixin):
cls.can_labeled_content_type.append(ct.id)
return cls.can_labeled_content_type
def get_queryset(self):
ids = self.get_can_labeled_content_type_ids()
queryset = ContentType.objects.filter(id__in=ids)
return queryset
return label_resource_types
@action(methods=['GET'], detail=True, serializer_class=serializers.ContentTypeResourceSerializer)
def resources(self, request, *args, **kwargs):
@ -62,6 +45,7 @@ class ContentTypeViewSet(JMSModelViewSet):
keyword = request.query_params.get('search')
if keyword:
queryset = content_type.filter_queryset(queryset, keyword)
queryset = queryset.order_by('res_type')
return self.get_paginated_response_from_queryset(queryset)
@ -145,3 +129,8 @@ class LabeledResourceViewSet(OrgBulkModelViewSet):
'default': serializers.LabeledResourceSerializer,
}
ordering_fields = ('res_type', 'date_created')
def get_queryset(self):
queryset = super().get_queryset()
queryset = queryset.order_by('res_type')
return queryset

View File

@ -0,0 +1,24 @@
from django.contrib.contenttypes.models import ContentType
from django.utils.functional import LazyObject
class LabeledResourceType(LazyObject):
@staticmethod
def get_res_types():
content_types = ContentType.objects.all()
ids = []
for ct in content_types:
model_cls = ct.model_class()
if not model_cls:
continue
if model_cls._meta.parents:
continue
if 'labels' in model_cls._meta._forward_fields_map.keys():
ids.append(ct.id)
return ContentType.objects.filter(id__in=ids)
def _setup(self):
self._wrapped = self.get_res_types()
label_resource_types = LabeledResourceType()

View File

@ -25,7 +25,9 @@ class Label(JMSOrgBaseModel):
class LabeledResource(JMSOrgBaseModel):
label = models.ForeignKey(Label, on_delete=models.CASCADE, related_name='labeled_resources')
label = models.ForeignKey(
Label, on_delete=models.CASCADE, related_name='labeled_resources', verbose_name=_("Label")
)
res_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
res_id = models.CharField(max_length=36, verbose_name=_("Resource ID"), db_index=True)
resource = GenericForeignKey('res_type', 'res_id')

View File

@ -1,10 +1,10 @@
from django.contrib.contenttypes.models import ContentType
from django.db.models import Count
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from common.serializers.fields import ObjectRelatedField
from common.serializers.fields import ObjectRelatedField, LabeledChoiceField
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from .const import label_resource_types
from .models import Label, LabeledResource
__all__ = ['LabelSerializer', 'LabeledResourceSerializer', 'ContentTypeResourceSerializer']
@ -27,10 +27,10 @@ class LabelSerializer(BulkOrgResourceModelSerializer):
class LabeledResourceSerializer(serializers.ModelSerializer):
res_type = ObjectRelatedField(
queryset=ContentType.objects, attrs=('app_label', 'model', 'name'), label=_("Resource type")
res_type = LabeledChoiceField(
choices=[], label=_("Resource type"), source='res_type_id', required=False
)
label = ObjectRelatedField(queryset=Label.objects, attrs=('name', 'value'))
label = ObjectRelatedField(queryset=Label.objects, attrs=('name', 'value'), label=_("Label"))
resource = serializers.CharField(label=_("Resource"))
class Meta:
@ -44,6 +44,17 @@ class LabeledResourceSerializer(serializers.ModelSerializer):
queryset = queryset.select_related('label', 'res_type')
return queryset
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.set_res_type_choices()
def set_res_type_choices(self):
res_type_field = self.fields.get('res_type')
if not res_type_field:
return
res_type_field.choices = [(ct.id, ct.name) for ct in label_resource_types]
class ContentTypeResourceSerializer(serializers.Serializer):
id = serializers.CharField()

View File

@ -1,12 +1,12 @@
import json
import os
from django.conf import settings
from django.db import transaction
from django.db.models import Count
from django.shortcuts import get_object_or_404
from django.utils._os import safe_join
from django.utils.translation import gettext_lazy as _
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.views import APIView
@ -167,7 +167,7 @@ class JobExecutionViewSet(OrgBulkModelViewSet):
@staticmethod
def start_deploy(instance, serializer):
task = run_ops_job_execution.apply_async((str(instance.id),), task_id=str(instance.id))
run_ops_job_execution.apply_async((str(instance.id),), task_id=str(instance.id))
def perform_create(self, serializer):
instance = serializer.save()
@ -179,7 +179,8 @@ class JobExecutionViewSet(OrgBulkModelViewSet):
set_task_to_serializer_data(serializer, instance.id)
transaction.on_commit(
lambda: run_ops_job_execution.apply_async((str(instance.id),), task_id=str(instance.id)))
lambda: run_ops_job_execution.apply_async((str(instance.id),), task_id=str(instance.id))
)
def get_queryset(self):
queryset = super().get_queryset()

View File

@ -22,7 +22,6 @@ from acls.models import CommandFilterACL
from assets.models import Asset
from assets.automations.base.manager import SSHTunnelManager
from common.db.encoder import ModelJSONFieldEncoder
from labels.mixins import LabeledMixin
from ops.ansible import JMSInventory, AdHocRunner, PlaybookRunner, CommandInBlackListException, UploadFileRunner
from ops.mixin import PeriodTaskModelMixin
from ops.variables import *
@ -133,19 +132,15 @@ class JMSPermedInventory(JMSInventory):
return mapper
class Job(LabeledMixin, JMSOrgBaseModel, PeriodTaskModelMixin):
class Job(JMSOrgBaseModel, PeriodTaskModelMixin):
name = models.CharField(max_length=128, null=True, verbose_name=_('Name'))
instant = models.BooleanField(default=False)
args = models.CharField(max_length=8192, default='', verbose_name=_('Args'), null=True, blank=True)
module = models.CharField(max_length=128, choices=JobModules.choices, default=JobModules.shell,
verbose_name=_('Module'),
null=True)
verbose_name=_('Module'), null=True)
chdir = models.CharField(default="", max_length=1024, verbose_name=_('Chdir'), null=True, blank=True)
timeout = models.IntegerField(default=-1, verbose_name=_('Timeout (Seconds)'))
playbook = models.ForeignKey('ops.Playbook', verbose_name=_("Playbook"), null=True, on_delete=models.SET_NULL)
type = models.CharField(max_length=128, choices=Types.choices, default=Types.adhoc, verbose_name=_("Type"))
creator = models.ForeignKey('users.User', verbose_name=_("Creator"), on_delete=models.SET_NULL, null=True)
assets = models.ManyToManyField('assets.Asset', verbose_name=_("Assets"))

View File

@ -6,7 +6,6 @@ from django.db import models
from django.utils.translation import gettext_lazy as _
from private_storage.fields import PrivateFileField
from labels.mixins import LabeledMixin
from ops.const import CreateMethods
from ops.exception import PlaybookNoValidEntry
from orgs.mixins.models import JMSOrgBaseModel
@ -24,7 +23,7 @@ dangerous_keywords = (
)
class Playbook(LabeledMixin, JMSOrgBaseModel):
class Playbook(JMSOrgBaseModel):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, verbose_name=_('Name'), null=True)
path = PrivateFileField(upload_to='playbooks/')

View File

@ -56,9 +56,22 @@ class RoleViewSet(JMSModelViewSet):
return
instance.permissions.set(clone.get_permissions())
def filter_builtins(self, queryset):
keyword = self.request.query_params.get('search')
if not keyword:
return queryset
builtins = list(self.queryset.filter(builtin=True))
matched = [role.id for role in builtins if keyword in role.display_name]
if not matched:
return queryset
queryset = list(queryset.exclude(id__in=matched))
return queryset + builtins
def filter_queryset(self, queryset):
queryset = super().filter_queryset(queryset)
queryset = queryset.order_by(*self.ordering)
queryset = self.filter_builtins(queryset)
return queryset
def set_users_amount(self, queryset):