mirror of https://github.com/jumpserver/jumpserver
perf: 优化 json error
parent
4e5ab5a605
commit
ebaa8d2637
|
@ -294,6 +294,29 @@ class RelatedManager:
|
|||
self.value = value
|
||||
self.instance.__dict__[self.field.name] = value
|
||||
|
||||
@classmethod
|
||||
def get_filter_q(cls, value, to_model):
|
||||
if not value or not isinstance(value, dict):
|
||||
return Q()
|
||||
|
||||
if value["type"] == "all":
|
||||
return Q()
|
||||
elif value["type"] == "ids" and isinstance(value.get("ids"), list):
|
||||
return Q(id__in=value["ids"])
|
||||
elif value["type"] == "attrs" and isinstance(value.get("attrs"), list):
|
||||
return cls._get_filter_attrs_q(value, to_model)
|
||||
else:
|
||||
return Q()
|
||||
|
||||
@classmethod
|
||||
def filter_queryset_by_model(cls, value, to_model):
|
||||
if hasattr(to_model, "get_queryset"):
|
||||
queryset = to_model.get_queryset()
|
||||
else:
|
||||
queryset = to_model.objects.all()
|
||||
q = cls.get_filter_q(value, to_model)
|
||||
return queryset.filter(q)
|
||||
|
||||
@staticmethod
|
||||
def get_ip_in_q(name, val):
|
||||
q = Q()
|
||||
|
@ -322,7 +345,8 @@ class RelatedManager:
|
|||
continue
|
||||
return q
|
||||
|
||||
def _get_filter_attrs_q(self, value, to_model):
|
||||
@classmethod
|
||||
def _get_filter_attrs_q(cls, value, to_model):
|
||||
filters = Q()
|
||||
# 特殊情况有这几种,
|
||||
# 1. 像 资产中的 type 和 category,集成自 Platform。所以不能直接查询
|
||||
|
@ -340,16 +364,14 @@ class RelatedManager:
|
|||
if name is None or val is None:
|
||||
continue
|
||||
|
||||
print("Has custom filter: {}".format(custom_attr_filter))
|
||||
if custom_attr_filter:
|
||||
custom_filter_q = custom_attr_filter(name, val, match)
|
||||
print("Custom filter: {}".format(custom_filter_q))
|
||||
if custom_filter_q:
|
||||
filters &= custom_filter_q
|
||||
continue
|
||||
|
||||
if match == 'ip_in':
|
||||
q = self.get_ip_in_q(name, val)
|
||||
q = cls.get_ip_in_q(name, val)
|
||||
elif match in ("exact", "contains", "startswith", "endswith", "regex", "gte", "lte", "gt", "lt"):
|
||||
lookup = "{}__{}".format(name, match)
|
||||
q = Q(**{lookup: val})
|
||||
|
@ -377,26 +399,10 @@ class RelatedManager:
|
|||
def _get_queryset(self):
|
||||
to_model = apps.get_model(self.field.to)
|
||||
value = self.value
|
||||
if hasattr(to_model, "get_queryset"):
|
||||
queryset = to_model.get_queryset()
|
||||
else:
|
||||
queryset = to_model.objects.all()
|
||||
|
||||
if not value or not isinstance(value, dict):
|
||||
return queryset.none()
|
||||
|
||||
if value["type"] == "all":
|
||||
return queryset
|
||||
elif value["type"] == "ids" and isinstance(value.get("ids"), list):
|
||||
return queryset.filter(id__in=value["ids"])
|
||||
elif value["type"] == "attrs" and isinstance(value.get("attrs"), list):
|
||||
q = self._get_filter_attrs_q(value, to_model)
|
||||
return queryset.filter(q)
|
||||
else:
|
||||
return queryset.none()
|
||||
return self.filter_queryset_by_model(value, to_model)
|
||||
|
||||
def get_attr_q(self):
|
||||
q = self._get_filter_attrs_q(self.value)
|
||||
q = self._get_filter_attrs_q(self.value, apps.get_model(self.field.to))
|
||||
return q
|
||||
|
||||
def all(self):
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import base64
|
||||
import json
|
||||
import logging
|
||||
|
||||
from django.core.cache import cache
|
||||
|
@ -18,6 +20,8 @@ __all__ = [
|
|||
"BaseFilterSet"
|
||||
]
|
||||
|
||||
from common.db.fields import RelatedManager
|
||||
|
||||
|
||||
class BaseFilterSet(drf_filters.FilterSet):
|
||||
def do_nothing(self, queryset, name, value):
|
||||
|
@ -183,3 +187,24 @@ class UUIDInFilter(drf_filters.BaseInFilter, drf_filters.UUIDFilter):
|
|||
|
||||
class NumberInFilter(drf_filters.BaseInFilter, drf_filters.NumberFilter):
|
||||
pass
|
||||
|
||||
|
||||
class AttrRulesFilter(filters.BaseFilterBackend):
|
||||
def get_schema_fields(self, view):
|
||||
return [
|
||||
coreapi.Field(
|
||||
name='attr_rules', location='query', required=False,
|
||||
type='string', example='/api/v1/users/users?attr_rules=jsonbase64',
|
||||
description='Filter by json like {"type": "attrs", "attrs": []} to base64'
|
||||
)
|
||||
]
|
||||
|
||||
def filter_queryset(self, request, queryset, view):
|
||||
attr_rules = request.query_params.get('attr_rules')
|
||||
if not attr_rules:
|
||||
return queryset
|
||||
|
||||
attr_rules = base64.b64decode(attr_rules.encode('utf-8'))
|
||||
attr_rules = json.loads(attr_rules)
|
||||
q = RelatedManager.get_filter_q(attr_rules, queryset.model)
|
||||
return queryset.filter(q)
|
||||
|
|
|
@ -7,8 +7,8 @@ from rest_framework.decorators import action
|
|||
from rest_framework.response import Response
|
||||
from rest_framework_bulk import BulkModelViewSet
|
||||
|
||||
from common.api import CommonApiMixin
|
||||
from common.api import SuggestionMixin
|
||||
from common.api import CommonApiMixin, SuggestionMixin
|
||||
from common.drf.filters import AttrRulesFilter
|
||||
from common.utils import get_logger
|
||||
from orgs.utils import current_org, tmp_to_root_org
|
||||
from rbac.models import Role, RoleBinding
|
||||
|
@ -18,10 +18,7 @@ from .. import serializers
|
|||
from ..filters import UserFilter
|
||||
from ..models import User
|
||||
from ..notifications import ResetMFAMsg
|
||||
from ..serializers import (
|
||||
UserSerializer,
|
||||
MiniUserSerializer, InviteSerializer
|
||||
)
|
||||
from ..serializers import UserSerializer, MiniUserSerializer, InviteSerializer
|
||||
from ..signals import post_user_create
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
@ -33,6 +30,7 @@ __all__ = [
|
|||
|
||||
class UserViewSet(CommonApiMixin, UserQuerysetMixin, SuggestionMixin, BulkModelViewSet):
|
||||
filterset_class = UserFilter
|
||||
extra_filter_backends = [AttrRulesFilter]
|
||||
search_fields = ('username', 'email', 'name')
|
||||
serializer_classes = {
|
||||
'default': UserSerializer,
|
||||
|
|
Loading…
Reference in New Issue