pref: 类型树支持资产

pull/9235/head
ibuler 2022-12-22 11:34:18 +08:00
parent 7ca2fdca89
commit e0e57a71aa
4 changed files with 52 additions and 62 deletions

View File

@ -1,6 +1,5 @@
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
from django.core.exceptions import PermissionDenied
from rest_framework.generics import get_object_or_404 from rest_framework.generics import get_object_or_404
from rest_framework.response import Response from rest_framework.response import Response
@ -12,44 +11,20 @@ from orgs.utils import current_org
from .mixin import SerializeToTreeNodeMixin from .mixin import SerializeToTreeNodeMixin
from .. import serializers from .. import serializers
from ..const import AllTypes from ..const import AllTypes
from ..models import Node from ..models import Node, Platform, Asset
logger = get_logger(__file__) logger = get_logger(__file__)
__all__ = [ __all__ = [
'NodeChildrenApi', 'NodeChildrenApi',
'NodeListAsTreeApi',
'NodeChildrenAsTreeApi', 'NodeChildrenAsTreeApi',
'CategoryTreeApi', 'CategoryTreeApi',
] ]
class NodeListAsTreeApi(generics.ListAPIView):
"""
获取节点列表树
[
{
"id": "",
"name": "",
"pId": "",
"meta": ""
}
]
"""
model = Node
serializer_class = TreeNodeSerializer
@staticmethod
def to_tree_queryset(queryset):
queryset = [node.as_tree_node() for node in queryset]
return queryset
def filter_queryset(self, queryset):
queryset = super().filter_queryset(queryset)
queryset = self.to_tree_queryset(queryset)
return queryset
class NodeChildrenApi(generics.ListCreateAPIView): class NodeChildrenApi(generics.ListCreateAPIView):
"""
节点的增删改查
"""
serializer_class = serializers.NodeSerializer serializer_class = serializers.NodeSerializer
search_fields = ('value',) search_fields = ('value',)
@ -141,33 +116,38 @@ class NodeChildrenAsTreeApi(SerializeToTreeNodeMixin, NodeChildrenApi):
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
nodes = self.filter_queryset(self.get_queryset()).order_by('value') nodes = self.filter_queryset(self.get_queryset()).order_by('value')
nodes = self.serialize_nodes(nodes, with_asset_amount=True) nodes = self.serialize_nodes(nodes, with_asset_amount=True)
assets = self.get_assets() assets = self.get_assets_as_node()
data = [*nodes, *assets] data = [*nodes, *assets]
return Response(data=data) return Response(data=data)
def get_assets(self): def get_assets_as_node(self):
include_assets = self.request.query_params.get('assets', '0') == '1' include_assets = self.request.query_params.get('assets', '0') == '1'
if not self.instance or not include_assets: if not self.instance or not include_assets:
return [] return []
assets = self.instance.get_assets().only( assets = self.instance.get_assets_for_tree()
"id", "name", "address", "platform_id",
"org_id", "is_active",
).prefetch_related('platform')
return self.serialize_assets(assets, self.instance.key) return self.serialize_assets(assets, self.instance.key)
class CategoryTreeApi(SerializeToTreeNodeMixin, generics.ListAPIView): class CategoryTreeApi(SerializeToTreeNodeMixin, generics.ListAPIView):
serializer_class = TreeNodeSerializer serializer_class = TreeNodeSerializer
rbac_perms = {
'GET': 'assets.view_asset',
'list': 'assets.view_asset',
}
def check_permissions(self, request): def get_assets(self):
if not request.user.has_perm('assets.view_asset'): key = self.request.query_params.get('key')
raise PermissionDenied platform = Platform.objects.filter(id=key).first()
return True if not platform:
return []
assets = Asset.objects.filter(platform=platform).prefetch_related('platform')
return self.serialize_assets(assets, key)
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
if request.query_params.get('key'): include_asset = bool(self.request.query_params.get('assets'))
nodes = []
if include_asset and self.request.query_params.get('key'):
nodes = self.get_assets()
else: else:
nodes = AllTypes.to_tree_nodes() nodes = AllTypes.to_tree_nodes(include_asset)
serializer = self.get_serializer(nodes, many=True) return Response(data=nodes)
return Response(data=serializer.data)

View File

@ -1,8 +1,9 @@
from collections import defaultdict from collections import defaultdict
from copy import deepcopy from copy import deepcopy
from django.utils.translation import gettext as _
from common.db.models import ChoicesMixin from common.db.models import ChoicesMixin
from common.tree import TreeNode
from .category import Category from .category import Category
from .cloud import CloudTypes from .cloud import CloudTypes
from .database import DatabaseTypes from .database import DatabaseTypes
@ -134,34 +135,34 @@ class AllTypes(ChoicesMixin):
@staticmethod @staticmethod
def choice_to_node(choice, pid, opened=True, is_parent=True, meta=None): def choice_to_node(choice, pid, opened=True, is_parent=True, meta=None):
node = TreeNode(**{ node = {
'id': pid + '_' + choice.name, 'id': pid + '_' + choice.name,
'name': choice.label, 'name': choice.label,
'title': choice.label, 'title': choice.label,
'pId': pid, 'pId': pid,
'open': opened, 'open': opened,
'isParent': is_parent, 'isParent': is_parent,
}) }
if meta: if meta:
node.meta = meta node['meta'] = meta
return node return node
@classmethod @classmethod
def platform_to_node(cls, p, pid): def platform_to_node(cls, p, pid, include_asset):
node = TreeNode(**{ node = {
'id': '{}'.format(p.id), 'id': '{}'.format(p.id),
'name': p.name, 'name': p.name,
'title': p.name, 'title': p.name,
'pId': pid, 'pId': pid,
'isParent': True, 'isParent': include_asset,
'meta': { 'meta': {
'type': 'platform' 'type': 'platform'
} }
}) }
return node return node
@classmethod @classmethod
def to_tree_nodes(cls): def to_tree_nodes(cls, include_asset):
from ..models import Asset, Platform from ..models import Asset, Platform
asset_platforms = Asset.objects.all().values_list('platform_id', flat=True) asset_platforms = Asset.objects.all().values_list('platform_id', flat=True)
platform_count = defaultdict(int) platform_count = defaultdict(int)
@ -177,26 +178,29 @@ class AllTypes(ChoicesMixin):
category_type_mapper[p.category] += platform_count[p.id] category_type_mapper[p.category] += platform_count[p.id]
tp_platforms[p.category + '_' + p.type].append(p) tp_platforms[p.category + '_' + p.type].append(p)
root = TreeNode(id='ROOT', name='所有类型', title='所有类型', open=True, isParent=True) root = dict(id='ROOT', name=_('All types'), title='所有类型', open=True, isParent=True)
nodes = [root] nodes = [root]
for category, type_cls in cls.category_types(): for category, type_cls in cls.category_types():
# Category 格式化
meta = {'type': 'category', 'category': category.value} meta = {'type': 'category', 'category': category.value}
category_node = cls.choice_to_node(category, 'ROOT', meta=meta) category_node = cls.choice_to_node(category, 'ROOT', meta=meta)
category_count = category_type_mapper.get(category, 0) category_count = category_type_mapper.get(category, 0)
category_node.name += f'({category_count})' category_node['name'] += f'({category_count})'
nodes.append(category_node) nodes.append(category_node)
tps = type_cls.get_types() # Type 格式化
for tp in tps: types = type_cls.get_types()
for tp in types:
meta = {'type': 'type', 'category': category.value, '_type': tp.value} meta = {'type': 'type', 'category': category.value, '_type': tp.value}
tp_node = cls.choice_to_node(tp, category_node.id, opened=False, meta=meta) tp_node = cls.choice_to_node(tp, category_node['id'], opened=False, meta=meta)
tp_count = category_type_mapper.get(category + '_' + tp, 0) tp_count = category_type_mapper.get(category + '_' + tp, 0)
tp_node.name += f'({tp_count})' tp_node['name'] += f'({tp_count})'
nodes.append(tp_node) nodes.append(tp_node)
# Platform 格式化
for p in tp_platforms.get(category + '_' + tp, []): for p in tp_platforms.get(category + '_' + tp, []):
platform_node = cls.platform_to_node(p, tp_node.id) platform_node = cls.platform_to_node(p, tp_node['id'], include_asset)
platform_node.name += f'({platform_count.get(p.id, 0)})' platform_node['name'] += f'({platform_count.get(p.id, 0)})'
nodes.append(platform_node) nodes.append(platform_node)
return nodes return nodes

View File

@ -430,6 +430,12 @@ class NodeAssetsMixin(NodeAllAssetsMappingMixin):
assets = Asset.objects.filter(nodes=self) assets = Asset.objects.filter(nodes=self)
return assets.distinct() return assets.distinct()
def get_assets_for_tree(self):
return self.get_assets().only(
"id", "name", "address", "platform_id",
"org_id", "is_active"
).prefetch_related('platform')
def get_valid_assets(self): def get_valid_assets(self):
return self.get_assets().valid() return self.get_assets().valid()

View File

@ -50,7 +50,7 @@ urlpatterns = [
name='account-secret-history'), name='account-secret-history'),
path('nodes/category/tree/', api.CategoryTreeApi.as_view(), name='asset-category-tree'), path('nodes/category/tree/', api.CategoryTreeApi.as_view(), name='asset-category-tree'),
path('nodes/tree/', api.NodeListAsTreeApi.as_view(), name='node-tree'), # path('nodes/tree/', api.NodeListAsTreeApi.as_view(), name='node-tree'),
path('nodes/children/tree/', api.NodeChildrenAsTreeApi.as_view(), name='node-children-tree'), path('nodes/children/tree/', api.NodeChildrenAsTreeApi.as_view(), name='node-children-tree'),
path('nodes/<uuid:pk>/children/', api.NodeChildrenApi.as_view(), name='node-children'), path('nodes/<uuid:pk>/children/', api.NodeChildrenApi.as_view(), name='node-children'),
path('nodes/children/', api.NodeChildrenApi.as_view(), name='node-children-2'), path('nodes/children/', api.NodeChildrenApi.as_view(), name='node-children-2'),