mirror of https://github.com/jumpserver/jumpserver
perf: JSONManyToMany 中的 m2m 方式支持包含所有
parent
dc841650cf
commit
ffa242e635
|
@ -286,24 +286,25 @@ class RelatedManager:
|
||||||
self.instance.__dict__[self.field.name] = value
|
self.instance.__dict__[self.field.name] = value
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_to_filter_q(cls, value, to_model):
|
def get_to_filter_qs(cls, value, to_model):
|
||||||
"""
|
"""
|
||||||
这个是 instance 去查找 to_model 的 queryset 的 Q
|
这个是 instance 去查找 to_model 的 queryset 的 Q
|
||||||
:param value:
|
:param value:
|
||||||
:param to_model:
|
:param to_model:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
default = [Q()]
|
||||||
if not value or not isinstance(value, dict):
|
if not value or not isinstance(value, dict):
|
||||||
return Q()
|
return default
|
||||||
|
|
||||||
if value["type"] == "all":
|
if value["type"] == "all":
|
||||||
return Q()
|
return default
|
||||||
elif value["type"] == "ids" and isinstance(value.get("ids"), list):
|
elif value["type"] == "ids" and isinstance(value.get("ids"), list):
|
||||||
return Q(id__in=value["ids"])
|
return [Q(id__in=value["ids"])]
|
||||||
elif value["type"] == "attrs" and isinstance(value.get("attrs"), list):
|
elif value["type"] == "attrs" and isinstance(value.get("attrs"), list):
|
||||||
return cls._get_filter_attrs_q(value, to_model)
|
return cls._get_filter_attrs_qs(value, to_model)
|
||||||
else:
|
else:
|
||||||
return Q()
|
return default
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def filter_queryset_by_model(cls, value, to_model):
|
def filter_queryset_by_model(cls, value, to_model):
|
||||||
|
@ -311,8 +312,10 @@ class RelatedManager:
|
||||||
queryset = to_model.get_queryset()
|
queryset = to_model.get_queryset()
|
||||||
else:
|
else:
|
||||||
queryset = to_model.objects.all()
|
queryset = to_model.objects.all()
|
||||||
q = cls.get_to_filter_q(value, to_model)
|
qs = cls.get_to_filter_qs(value, to_model)
|
||||||
return queryset.filter(q).distinct()
|
for q in qs:
|
||||||
|
queryset = queryset.filter(q)
|
||||||
|
return queryset.distinct()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_ip_in_q(name, val):
|
def get_ip_in_q(name, val):
|
||||||
|
@ -343,8 +346,8 @@ class RelatedManager:
|
||||||
return q
|
return q
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_filter_attrs_q(cls, value, to_model):
|
def _get_filter_attrs_qs(cls, value, to_model):
|
||||||
filters = Q()
|
filters = []
|
||||||
# 特殊情况有这几种,
|
# 特殊情况有这几种,
|
||||||
# 1. 像 资产中的 type 和 category,集成自 Platform。所以不能直接查询
|
# 1. 像 资产中的 type 和 category,集成自 Platform。所以不能直接查询
|
||||||
# 2. 像 资产中的 nodes,不是简单的 m2m,是树 的关系
|
# 2. 像 资产中的 nodes,不是简单的 m2m,是树 的关系
|
||||||
|
@ -364,7 +367,7 @@ class RelatedManager:
|
||||||
if custom_attr_filter:
|
if custom_attr_filter:
|
||||||
custom_filter_q = custom_attr_filter(name, val, match)
|
custom_filter_q = custom_attr_filter(name, val, match)
|
||||||
if custom_filter_q:
|
if custom_filter_q:
|
||||||
filters &= custom_filter_q
|
filters.append(custom_filter_q)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if match == 'ip_in':
|
if match == 'ip_in':
|
||||||
|
@ -381,13 +384,22 @@ class RelatedManager:
|
||||||
q = Q(pk__isnull=True)
|
q = Q(pk__isnull=True)
|
||||||
elif match == "not":
|
elif match == "not":
|
||||||
q = ~Q(**{name: val})
|
q = ~Q(**{name: val})
|
||||||
elif match in ['m2m', 'in']:
|
elif match.startswith('m2m'):
|
||||||
|
if not isinstance(val, list):
|
||||||
|
val = [val]
|
||||||
|
if match == 'm2m_all':
|
||||||
|
for v in val:
|
||||||
|
filters.append(Q(**{"{}__in".format(name): [v]}))
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
q = Q(**{"{}__in".format(name): val})
|
||||||
|
elif match == 'in':
|
||||||
if not isinstance(val, list):
|
if not isinstance(val, list):
|
||||||
val = [val]
|
val = [val]
|
||||||
q = Q() if '*' in val else Q(**{"{}__in".format(name): val})
|
q = Q() if '*' in val else Q(**{"{}__in".format(name): val})
|
||||||
else:
|
else:
|
||||||
q = Q() if val == '*' else Q(**{name: val})
|
q = Q() if val == '*' else Q(**{name: val})
|
||||||
filters &= q
|
filters.append(q)
|
||||||
return filters
|
return filters
|
||||||
|
|
||||||
def _get_queryset(self):
|
def _get_queryset(self):
|
||||||
|
@ -397,8 +409,8 @@ class RelatedManager:
|
||||||
|
|
||||||
def get_attr_q(self):
|
def get_attr_q(self):
|
||||||
to_model = apps.get_model(self.field.to)
|
to_model = apps.get_model(self.field.to)
|
||||||
q = self._get_filter_attrs_q(self.value, to_model)
|
qs = self._get_filter_attrs_qs(self.value, to_model)
|
||||||
return q
|
return qs
|
||||||
|
|
||||||
def all(self):
|
def all(self):
|
||||||
return self._get_queryset()
|
return self._get_queryset()
|
||||||
|
@ -491,7 +503,7 @@ class JSONManyToManyDescriptor:
|
||||||
if isinstance(rule_value, str):
|
if isinstance(rule_value, str):
|
||||||
rule_value = [rule_value]
|
rule_value = [rule_value]
|
||||||
res &= '*' in rule_value or contains_ip(value, rule_value)
|
res &= '*' in rule_value or contains_ip(value, rule_value)
|
||||||
elif rule['match'] == 'm2m':
|
elif rule['match'].startswith('m2m'):
|
||||||
if isinstance(value, Manager):
|
if isinstance(value, Manager):
|
||||||
value = value.values_list('id', flat=True)
|
value = value.values_list('id', flat=True)
|
||||||
elif isinstance(value, QuerySet):
|
elif isinstance(value, QuerySet):
|
||||||
|
@ -502,7 +514,12 @@ class JSONManyToManyDescriptor:
|
||||||
rule_value = [rule_value]
|
rule_value = [rule_value]
|
||||||
value = set(map(str, value))
|
value = set(map(str, value))
|
||||||
rule_value = set(map(str, rule_value))
|
rule_value = set(map(str, rule_value))
|
||||||
|
|
||||||
|
if rule['match'] == 'm2m_all':
|
||||||
|
res &= rule_value.issubset(value)
|
||||||
|
else:
|
||||||
res &= bool(value & rule_value)
|
res &= bool(value & rule_value)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logging.error("unknown match: {}".format(rule['match']))
|
logging.error("unknown match: {}".format(rule['match']))
|
||||||
res &= False
|
res &= False
|
||||||
|
|
|
@ -235,8 +235,10 @@ class AttrRulesFilterBackend(filters.BaseFilterBackend):
|
||||||
raise ValidationError({'attr_rules': 'attr_rules should be json'})
|
raise ValidationError({'attr_rules': 'attr_rules should be json'})
|
||||||
|
|
||||||
logger.debug('attr_rules: %s', attr_rules)
|
logger.debug('attr_rules: %s', attr_rules)
|
||||||
q = RelatedManager.get_to_filter_q(attr_rules, queryset.model)
|
qs = RelatedManager.get_to_filter_qs(attr_rules, queryset.model)
|
||||||
return queryset.filter(q).distinct()
|
for q in qs:
|
||||||
|
queryset = queryset.filter(q)
|
||||||
|
return queryset.distinct()
|
||||||
|
|
||||||
|
|
||||||
class NotOrRelFilterBackend(filters.BaseFilterBackend):
|
class NotOrRelFilterBackend(filters.BaseFilterBackend):
|
||||||
|
|
Loading…
Reference in New Issue