优化部分接口查询、n+1问题, 添加logs

This commit is contained in:
xie7654
2025-07-06 21:40:38 +08:00
parent 575513ccbb
commit 3c748eacd6
8 changed files with 98 additions and 15 deletions

View File

@@ -23,7 +23,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = 'django-insecure-m4@pv814c_m^pgpyhz^i96a@mcqh_@m9ccu(17*895t!79e!nb'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = os.getenv('DEBUG', 'False') == 'True'
DEBUG = os.getenv('DEBUG', 'True') == 'True'
# 演示环境配置
DEMO_MODE = os.getenv('DEMO_MODE', 'False') == 'False'
@@ -208,5 +208,27 @@ CELERY_BEAT_SCHEDULE = {
}
# celery 配置结束
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'ignore_auth_user': {
'()': 'utils.filters_logs.IgnoreSQLFilter',
}
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'filters': ['ignore_auth_user'],
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'level': 'DEBUG', # 只在 DEBUG 模式时生效
},
}
}
if os.path.exists(os.path.join(BASE_DIR, 'backend/local_settings.py')):
from backend.local_settings import *

View File

@@ -1,10 +1,11 @@
from collections import defaultdict
from datetime import timezone, datetime
from django.db import models
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import status, serializers
from rest_framework.decorators import action
from rest_framework.filters import SearchFilter, OrderingFilter
from rest_framework.response import Response
from system.models import Dept
from utils.custom_model_viewSet import CustomModelViewSet
@@ -23,9 +24,9 @@ class DeptSerializer(CustomModelSerializer):
def get_children(self, obj):
"""获取子部门"""
children = obj.children.all().order_by('id')
children = getattr(obj, 'children', [])
if children:
return DeptSerializer(children, many=True).data
return DeptSerializer(children.all(), many=True).data
return []
def get_status_text(self, obj):
@@ -35,7 +36,9 @@ class DeptSerializer(CustomModelSerializer):
class DeptViewSet(CustomModelViewSet):
"""部门管理视图集"""
queryset = Dept.objects.filter(pid__isnull=True).order_by('id', 'status')
queryset = Dept.objects.filter(pid__isnull=True).order_by('id', 'status').select_related('pid').prefetch_related(
models.Prefetch('children', queryset=Dept.objects.order_by('id'))
)
serializer_class = DeptSerializer
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_fields = ['status', 'pid']
@@ -78,8 +81,39 @@ class DeptViewSet(CustomModelViewSet):
@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)
"""一次性查出所有部门,构建树形结构"""
all_depts = Dept.objects.all().order_by('sort', 'id')
dept_dict = {}
children_map = defaultdict(list)
# 先序列化为 dict不递归
for dept in all_depts:
item = {
'id': dept.id,
'pid': dept.pid_id,
'name': dept.name,
'status': dept.status,
'status_text': dept.get_status_display(),
'create_time': dept.create_time,
'sort': dept.sort,
'leader': dept.leader,
'phone': dept.phone,
'email': dept.email,
'remark': dept.remark,
'children': [],
}
dept_dict[dept.id] = item
if dept.pid_id:
children_map[dept.pid_id].append(item)
# 构建树
for dept_id, dept in dept_dict.items():
dept['children'] = children_map.get(dept_id, [])
# 返回 pid=None根部门
tree = [dept for dept in dept_dict.values() if dept['pid'] is None]
return self._build_response(
data=tree,
message="ok",
status=status.HTTP_200_OK,
)

View File

@@ -91,7 +91,7 @@ class MenuMetaViewSet(viewsets.ModelViewSet):
class MenuViewSet(CustomModelViewSet):
"""菜单管理视图集"""
queryset = Menu.objects.filter(pid__isnull=True).order_by('sort', 'id', 'status')
queryset = Menu.objects.filter(pid__isnull=True).order_by('sort', 'id', 'status').prefetch_related('children')
serializer_class = MenuSerializer
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_fields = ['status', 'type', 'pid', 'name']

View File

@@ -42,7 +42,7 @@ class RoleFilter(filters.FilterSet):
class RoleViewSet(CustomModelViewSet):
"""角色管理视图集"""
queryset = Role.objects.all()
queryset = Role.objects.all().prefetch_related('permissions')
serializer_class = RoleSerializer
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_class = RoleFilter

View File

@@ -1,3 +1,4 @@
from django.db.models import Prefetch, F
from django.utils import timezone
from rest_framework import serializers
from rest_framework.authtoken.models import Token
@@ -152,7 +153,7 @@ class UserViewSet(CustomModelViewSet):
"""
用户数据 视图集
"""
queryset = User.objects.filter(is_deleted=False).order_by('-id')
queryset = User.objects.filter(is_deleted=False).order_by('-id').prefetch_related('role', 'dept', 'post')
serializer_class = UserSerializer
read_only_fields = ['id', 'create_time', 'update_time', 'login_ip']
filterset_class = UserFilter

View File

@@ -0,0 +1,19 @@
import logging
class IgnoreSQLFilter(logging.Filter):
def __init__(self, name=''):
super().__init__(name)
# 后续只要在这里添加关键字即可
self.ignore_keywords = [
'authtoken_token',
'@@lower_case_table_names',
'SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED',
]
def filter(self, record):
sql = record.getMessage().strip().lower()
# 如果包含任何一个关键字,则返回 False不打印
for keyword in self.ignore_keywords:
if keyword.lower() in sql:
return False
return True

View File

@@ -53,4 +53,11 @@ async function deleteDept(id: string) {
return requestClient.delete(`/system/dept/${id}/`);
}
export { createDept, deleteDept, getDeptList, updateDept };
/**
* 查询tree
*/
async function treeDept() {
return requestClient.get(`/system/dept/tree/`);
}
export { createDept, deleteDept, getDeptList, treeDept, updateDept };

View File

@@ -11,7 +11,7 @@ import { Plus } from '@vben/icons';
import { Button, message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteDept, getDeptList } from '#/api/system/dept';
import { deleteDept, treeDept } from '#/api/system/dept';
import { $t } from '#/locales';
import { useColumns } from './data';
@@ -108,7 +108,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
proxyConfig: {
ajax: {
query: async (_params) => {
return await getDeptList();
return await treeDept();
},
},
},