perf: 优化 labels 支持多个搜索 (#12367)

Co-authored-by: ibuler <ibuler@qq.com>
pull/12376/head
fit2bot 2023-12-19 18:46:02 +08:00 committed by GitHub
parent fda3e6ec9b
commit 45425b11d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 53 additions and 15 deletions

View File

@ -6,6 +6,7 @@ import logging
from django.core.cache import cache
from django.core.exceptions import ImproperlyConfigured
from django.db.models import Q, Count
from django_filters import rest_framework as drf_filters
from rest_framework import filters
from rest_framework.compat import coreapi, coreschema
@ -178,9 +179,41 @@ class LabelFilterBackend(filters.BaseFilterBackend):
)
]
@staticmethod
def filter_resources(resources, labels_id):
label_ids = [i.strip() for i in labels_id.split(',')]
args = []
for label_id in label_ids:
kwargs = {}
if ':' in label_id:
k, v = label_id.split(':', 1)
kwargs['label__name'] = k.strip()
if v != '*':
kwargs['label__value'] = v.strip()
else:
kwargs['label_id'] = label_id
args.append(kwargs)
if len(args) == 1:
resources = resources.filter(**args[0])
return resources
q = Q()
for kwarg in args:
q |= Q(**kwarg)
resources = resources.filter(q) \
.values('res_id') \
.order_by('res_id') \
.annotate(count=Count('res_id')) \
.values('res_id', 'count') \
.filter(count=len(args))
return resources
def filter_queryset(self, request, queryset, view):
label_id = request.query_params.get('label')
if not label_id:
labels_id = request.query_params.get('labels')
if not labels_id:
return queryset
if not hasattr(queryset, 'model'):
@ -189,23 +222,16 @@ class LabelFilterBackend(filters.BaseFilterBackend):
if not hasattr(queryset.model, 'labels'):
return queryset
kwargs = {}
if ':' in label_id:
k, v = label_id.split(':', 1)
kwargs['label__name'] = k.strip()
if v != '*':
kwargs['label__value'] = v.strip()
else:
kwargs['label_id'] = label_id
model = queryset.model
labeled_resource_cls = model.labels.field.related_model
app_label = model._meta.app_label
model_name = model._meta.model_name
res_ids = labeled_resource_cls.objects.filter(
resources = labeled_resource_cls.objects.filter(
res_type__app_label=app_label, res_type__model=model_name,
).filter(**kwargs).values_list('res_id', flat=True)
)
resources = self.filter_resources(resources, labels_id)
res_ids = resources.values_list('res_id', flat=True)
queryset = queryset.filter(id__in=set(res_ids))
return queryset

View File

@ -13,12 +13,24 @@ __all__ = ['LabelSerializer', 'LabeledResourceSerializer', 'ContentTypeResourceS
class LabelSerializer(BulkOrgResourceModelSerializer):
class Meta:
model = Label
fields = ['id', 'name', 'value', 'res_count', 'comment', 'date_created', 'date_updated']
fields = [
'id', 'name', 'value', 'res_count', 'comment',
'date_created', 'date_updated'
]
read_only_fields = ('date_created', 'date_updated', 'res_count')
extra_kwargs = {
'res_count': {'label': _('Resource count')},
}
@staticmethod
def validate_name(value):
if ':' in value or ',' in value:
raise serializers.ValidationError(_('Cannot contain ":,"'))
return value
def validate_value(self, value):
return self.validate_name(value)
@classmethod
def setup_eager_loading(cls, queryset):
""" Perform necessary eager loading of data. """

View File

@ -61,7 +61,7 @@ class RoleViewSet(JMSModelViewSet):
if not keyword:
return queryset
builtins = list(self.queryset.filter(builtin=True))
builtins = list(self.get_queryset().filter(builtin=True))
matched = [role.id for role in builtins if keyword in role.display_name]
if not matched:
return queryset