diff --git a/apps/common/drf/filters.py b/apps/common/drf/filters.py index 6df636fdf..103eef212 100644 --- a/apps/common/drf/filters.py +++ b/apps/common/drf/filters.py @@ -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 diff --git a/apps/labels/serializers.py b/apps/labels/serializers.py index 60b0ad4b1..bd4d09b90 100644 --- a/apps/labels/serializers.py +++ b/apps/labels/serializers.py @@ -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. """ diff --git a/apps/rbac/api/role.py b/apps/rbac/api/role.py index 093c79206..b9133c5c5 100644 --- a/apps/rbac/api/role.py +++ b/apps/rbac/api/role.py @@ -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