from django_filters.rest_framework import DjangoFilterBackend from rest_framework import viewsets, serializers, status from rest_framework.decorators import action from rest_framework.filters import SearchFilter, OrderingFilter from rest_framework.response import Response from system.models import Menu, MenuMeta from utils.custom_model_viewSet import CustomModelViewSet from utils.serializers import CustomModelSerializer class MenuMetaSerializer(serializers.ModelSerializer): """菜单元数据序列化器""" hideChildrenInMenu = serializers.SerializerMethodField() hideInMenu = serializers.SerializerMethodField() class Meta: model = MenuMeta fields = '__all__' def get_hideChildrenInMenu(self, obj): return getattr(obj, 'hide_children_in_menu', None) def get_hideInMenu(self, obj): return getattr(obj, 'hide_in_menu', None) class MenuSerializer(CustomModelSerializer): """菜单序列化器""" parent = serializers.CharField(source='pid.name', read_only=True) meta = MenuMetaSerializer() children = serializers.SerializerMethodField() status_text = serializers.SerializerMethodField() type_text = serializers.SerializerMethodField() class Meta: model = Menu fields = '__all__' read_only_fields = ['id', 'create_time', 'update_time'] def get_children(self, obj): """获取子菜单""" children = obj.children.all().order_by('sort') if children: return MenuSerializer(children, many=True).data return [] def get_status_text(self, obj): """获取状态文本""" return obj.get_status_display() def get_type_text(self, obj): """获取菜单类型文本""" return obj.get_type_display() def create(self, validated_data): """创建菜单及关联的元数据""" meta_data = validated_data.pop('meta') meta = MenuMeta.objects.create(**meta_data) self.set_audit_user_fields(validated_data, is_create=True) menu = Menu.objects.create(meta=meta, **validated_data) return menu def update(self, instance, validated_data): """更新菜单及关联的元数据""" self.set_audit_user_fields(validated_data, is_create=False) meta_data = validated_data.pop('meta', {}) meta_serializer = self.fields['meta'] meta_serializer.update(instance.meta, meta_data) return super().update(instance, validated_data) class MenuUserSerializer(MenuSerializer): def get_children(self, obj): request = self.context.get('request') children_qs = obj.children.exclude(type='button').order_by('sort') if request and hasattr(request, 'user') and request.user.is_authenticated and not request.user.is_superuser: role_ids = request.user.role.values_list('id', flat=True) children_qs = children_qs.filter(role__id__in=role_ids).distinct() if children_qs: return MenuUserSerializer(children_qs, many=True, context=self.context).data return [] class MenuMetaViewSet(viewsets.ModelViewSet): """菜单元数据视图集""" queryset = MenuMeta.objects.all() serializer_class = MenuMetaSerializer class MenuViewSet(CustomModelViewSet): """菜单管理视图集""" queryset = Menu.objects.filter(pid__isnull=True).order_by('sort', 'id', 'status') serializer_class = MenuSerializer filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] filterset_fields = ['status', 'type', 'pid', 'name'] search_fields = ['name', 'path', 'auth_code'] ordering_fields = ['meta__sort', 'create_time'] @action(detail=False, methods=['get']) def tree(self, request): """获取菜单树形结构""" queryset = self.get_queryset().filter(pid__isnull=True) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) @action(detail=False, methods=['get'], url_path='name-exists') def name_exists(self, request): return self._build_response() @action(detail=False, methods=['get'], url_path='name-search') def name_search(self, request): name = request.GET.get('name') pk = request.GET.get('id', None) pid = request.GET.get('pid', None) queryset = Menu.objects.all() if pid: queryset = queryset.filter(pid=pid) if pk: queryset = queryset.exclude(pk=pk) if name: queryset = queryset.filter(name=name) has_menu_name = queryset.exists() return self._build_response(data=has_menu_name) @action(detail=False, methods=['get'], url_path='path-exists') def path_exists(self, request): return self._build_response() @action(detail=False, methods=['get'], url_path='user_menu') def user_menu(self, request): user = self.request.user if user.is_superuser: menus = Menu.objects.filter(pid__isnull=True).exclude(type='button').order_by('sort') else: role_ids = user.role.values_list('id', flat=True) menus = Menu.objects.filter(pid__isnull=True, role__id__in=role_ids ).exclude(type='button').order_by('sort').distinct() menus_data = MenuUserSerializer(menus, many=True, context={'request': request}).data return self._build_response(data=menus_data) def update(self, request, *args, **kwargs): partial = kwargs.pop('partial', False) pk = kwargs['pk'] instance = Menu.objects.get(pk=pk) serializer = self.get_serializer(instance, data=request.data, partial=partial) serializer.is_valid(raise_exception=True) self.perform_update(serializer) if getattr(instance, '_prefetched_objects_cache', None): # If 'prefetch_related' has been applied to a queryset, we need to # forcibly invalidate the prefetch cache on the instance. instance._prefetched_objects_cache = {} return self._build_response( data=serializer.data, message="ok", status=status.HTTP_200_OK, ) def destroy(self, request, *args, **kwargs): pk = kwargs['pk'] instance = Menu.objects.get(pk=pk) self.perform_destroy(instance) return self._build_response( message="ok", status=status.HTTP_200_OK, )