diff --git a/apps/assets/api/node.py b/apps/assets/api/node.py index 831c85e8a..73313f222 100644 --- a/apps/assets/api/node.py +++ b/apps/assets/api/node.py @@ -20,11 +20,10 @@ from rest_framework.response import Response from rest_framework_bulk import BulkModelViewSet from django.utils.translation import ugettext_lazy as _ from django.shortcuts import get_object_or_404 -from django.db.models import Count from common.utils import get_logger, get_object_or_none from ..hands import IsOrgAdmin -from ..models import Node +from ..models import Node, Asset from ..tasks import update_assets_hardware_info_util, test_asset_connectability_util from .. import serializers diff --git a/apps/assets/models/node.py b/apps/assets/models/node.py index af4ffd839..47a835861 100644 --- a/apps/assets/models/node.py +++ b/apps/assets/models/node.py @@ -22,7 +22,9 @@ class Node(OrgModelMixin): date_create = models.DateTimeField(auto_now_add=True) is_node = True - _full_value_cache_key_prefix = '_NODE_VALUE_{}' + _assets_amount = None + _full_value_cache_key = '_NODE_VALUE_{}' + _assets_amount_cache_key = '_NODE_ASSETS_AMOUNT_{}' class Meta: verbose_name = _("Node") @@ -49,30 +51,56 @@ class Node(OrgModelMixin): def name(self): return self.value + @property + def assets_amount(self): + """ + 获取节点下所有资产数量速度太慢,所以需要重写,使用cache等方案 + :return: + """ + if self._assets_amount is not None: + return self._assets_amount + cache_key = self._assets_amount_cache_key.format(self.key) + cached = cache.get(cache_key) + if cached is not None: + return cached + assets_amount = self.get_all_assets().count() + cache.set(cache_key, assets_amount, 3600) + return assets_amount + + @assets_amount.setter + def assets_amount(self, value): + self._assets_amount = value + + def expire_assets_amount(self): + ancestor_keys = self.get_ancestor_keys(with_self=True) + cache_keys = [self._assets_amount_cache_key.format(k) for k in ancestor_keys] + cache.delete_many(cache_keys) + + @classmethod + def expire_nodes_assets_amount(cls, nodes=None): + if nodes: + for node in nodes: + node.expire_assets_amount() + return + key = cls._assets_amount_cache_key.format('*') + cache.delete_pattern(key) + @property def full_value(self): - key = self._full_value_cache_key_prefix.format(self.key) + key = self._full_value_cache_key.format(self.key) cached = cache.get(key) if cached: return cached - value = self.get_full_value() - self.cache_full_value(value) - return value - - def get_full_value(self): - # ancestor = [a.value for a in self.get_ancestor(with_self=True)] if self.is_root(): return self.value parent_full_value = self.parent.full_value value = parent_full_value + ' / ' + self.value - return value - - def cache_full_value(self, value): - key = self._full_value_cache_key_prefix.format(self.key) + key = self._full_value_cache_key.format(self.key) cache.set(key, value, 3600) + return value def expire_full_value(self): - key = self._full_value_cache_key_prefix.format(self.key) + key = self._full_value_cache_key.format(self.key) cache.delete_pattern(key+'*') @property @@ -182,17 +210,18 @@ class Node(OrgModelMixin): child.save() self.save() - def get_ancestor(self, with_self=False): - if self.is_root(): - root = self.__class__.root() - return [root] - _key = self.key.split(':') + def get_ancestor_keys(self, with_self=False): + parent_keys = [] + key_list = self.key.split(":") if not with_self: - _key.pop() - ancestor_keys = [] - for i in range(len(_key)): - ancestor_keys.append(':'.join(_key)) - _key.pop() + key_list.pop() + for i in range(len(key_list)): + parent_keys.append(":".join(key_list)) + key_list.pop() + return parent_keys + + def get_ancestor(self, with_self=False): + ancestor_keys = self.get_ancestor_keys(with_self=with_self) ancestor = self.__class__.objects.filter( key__in=ancestor_keys ).order_by('key') @@ -227,10 +256,6 @@ class Node(OrgModelMixin): defaults = {'value': 'Default'} return cls.objects.get_or_create(defaults=defaults, key='1') - @classmethod - def get_tree_name_ref(cls): - pass - @classmethod def generate_fake(cls, count=100): import random diff --git a/apps/assets/serializers/node.py b/apps/assets/serializers/node.py index f1be42d06..f44ff44d6 100644 --- a/apps/assets/serializers/node.py +++ b/apps/assets/serializers/node.py @@ -43,7 +43,7 @@ class NodeGrantedSerializer(BulkSerializerMixin, serializers.ModelSerializer): class NodeSerializer(serializers.ModelSerializer): - assets_amount = serializers.SerializerMethodField() + assets_amount = serializers.IntegerField() tree_id = serializers.SerializerMethodField() tree_parent = serializers.SerializerMethodField() @@ -53,6 +53,10 @@ class NodeSerializer(serializers.ModelSerializer): 'id', 'key', 'value', 'assets_amount', 'is_node', 'org_id', 'tree_id', 'tree_parent', ] + read_only_fields = [ + 'id', 'key', 'assets_amount', 'is_node', + 'org_id', + ] list_serializer_class = BulkListSerializer def validate(self, data): @@ -66,12 +70,6 @@ class NodeSerializer(serializers.ModelSerializer): ) return data - @staticmethod - def get_assets_amount(obj): - if hasattr(obj, 'assets_amount'): - return obj.assets_amount - return obj.get_all_assets().count() - @staticmethod def get_tree_id(obj): return obj.key @@ -80,12 +78,6 @@ class NodeSerializer(serializers.ModelSerializer): def get_tree_parent(obj): return obj.parent_key - def get_fields(self): - fields = super().get_fields() - field = fields["key"] - field.required = False - return fields - class NodeAssetsSerializer(serializers.ModelSerializer): assets = serializers.PrimaryKeyRelatedField(many=True, queryset=Asset.objects.all()) diff --git a/apps/assets/signals_handler.py b/apps/assets/signals_handler.py index 9028f52c3..08ee6e670 100644 --- a/apps/assets/signals_handler.py +++ b/apps/assets/signals_handler.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # from collections import defaultdict -from django.db.models.signals import post_save, m2m_changed +from django.db.models.signals import post_save, m2m_changed, post_delete from django.dispatch import receiver from common.utils import get_logger @@ -35,6 +35,17 @@ def on_asset_created_or_update(sender, instance=None, created=False, **kwargs): update_asset_hardware_info_on_created(instance) test_asset_conn_on_created(instance) + # 过期节点资产数量 + nodes = instance.nodes.all() + Node.expire_nodes_assets_amount(nodes) + + +@receiver(post_delete, sender=Asset, dispatch_uid="my_unique_identifier") +def on_asset_delete(sender, instance=None, **kwargs): + # 过期节点资产数量 + nodes = instance.nodes.all() + Node.expire_nodes_assets_amount(nodes) + @receiver(post_save, sender=SystemUser, dispatch_uid="my_unique_identifier") def on_system_user_update(sender, instance=None, created=True, **kwargs): @@ -63,10 +74,11 @@ def on_system_user_assets_change(sender, instance=None, **kwargs): @receiver(m2m_changed, sender=Asset.nodes.through) def on_asset_node_changed(sender, instance=None, **kwargs): + logger.debug("Asset node change signal received") if isinstance(instance, Asset): if kwargs['action'] == 'post_add': - logger.debug("Asset node change signal received") nodes = kwargs['model'].objects.filter(pk__in=kwargs['pk_set']) + Node.expire_nodes_assets_amount(nodes) system_users_assets = defaultdict(set) system_users = SystemUser.objects.filter(nodes__in=nodes) # 清理节点缓存 @@ -79,9 +91,11 @@ def on_asset_node_changed(sender, instance=None, **kwargs): @receiver(m2m_changed, sender=Asset.nodes.through) def on_node_assets_changed(sender, instance=None, **kwargs): if isinstance(instance, Node): + logger.debug("Node assets change signal received") + # 当节点和资产关系发生改变时,过期资产数量缓存 + instance.expire_assets_amount() assets = kwargs['model'].objects.filter(pk__in=kwargs['pk_set']) if kwargs['action'] == 'post_add': - logger.debug("Node assets change signal received") # 重新关联系统用户和资产的关系 system_users = SystemUser.objects.filter(nodes=instance) for system_user in system_users: diff --git a/apps/assets/templates/assets/_asset_list_modal.html b/apps/assets/templates/assets/_asset_list_modal.html index ea8d59e49..fe50ac2b3 100644 --- a/apps/assets/templates/assets/_asset_list_modal.html +++ b/apps/assets/templates/assets/_asset_list_modal.html @@ -116,6 +116,7 @@ function initTree2() { $(document).ready(function(){ +}).on('show.bs.modal', function () { initTable2(); initTree2(); }) diff --git a/apps/assets/templates/assets/asset_list.html b/apps/assets/templates/assets/asset_list.html index 7e093688f..bd3a776b3 100644 --- a/apps/assets/templates/assets/asset_list.html +++ b/apps/assets/templates/assets/asset_list.html @@ -305,6 +305,9 @@ function onSelected(event, treeNode) { } function selectQueryNode() { + // TODO: 是否应该添加 + // 暂时忽略之前选中的内容 + return var query_node_id = $.getUrlParam("node"); var cookie_node_id = getCookie('node_selected'); var node; @@ -355,6 +358,9 @@ function onDrop(event, treeId, treeNodes, targetNode, moveType) { } function initTree() { + if (zTree) { + return + } var setting = { view: { dblClickExpand: false, @@ -387,6 +393,7 @@ function initTree() { }; var zNodes = []; + console.log("Get assets") $.get("{% url 'api-assets:node-list' %}", function(data, status){ $.each(data, function (index, value) { value["node_id"] = value["id"];