优化部分接口查询、n+1问题, 添加logs
This commit is contained in:
@@ -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'
|
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!
|
# 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'
|
DEMO_MODE = os.getenv('DEMO_MODE', 'False') == 'False'
|
||||||
@@ -208,5 +208,27 @@ CELERY_BEAT_SCHEDULE = {
|
|||||||
}
|
}
|
||||||
# celery 配置结束
|
# 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')):
|
if os.path.exists(os.path.join(BASE_DIR, 'backend/local_settings.py')):
|
||||||
from backend.local_settings import *
|
from backend.local_settings import *
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
|
from collections import defaultdict
|
||||||
from datetime import timezone, datetime
|
from datetime import timezone, datetime
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework import status, serializers
|
from rest_framework import status, serializers
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.filters import SearchFilter, OrderingFilter
|
from rest_framework.filters import SearchFilter, OrderingFilter
|
||||||
from rest_framework.response import Response
|
|
||||||
|
|
||||||
from system.models import Dept
|
from system.models import Dept
|
||||||
from utils.custom_model_viewSet import CustomModelViewSet
|
from utils.custom_model_viewSet import CustomModelViewSet
|
||||||
@@ -23,9 +24,9 @@ class DeptSerializer(CustomModelSerializer):
|
|||||||
|
|
||||||
def get_children(self, obj):
|
def get_children(self, obj):
|
||||||
"""获取子部门"""
|
"""获取子部门"""
|
||||||
children = obj.children.all().order_by('id')
|
children = getattr(obj, 'children', [])
|
||||||
if children:
|
if children:
|
||||||
return DeptSerializer(children, many=True).data
|
return DeptSerializer(children.all(), many=True).data
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def get_status_text(self, obj):
|
def get_status_text(self, obj):
|
||||||
@@ -35,7 +36,9 @@ class DeptSerializer(CustomModelSerializer):
|
|||||||
|
|
||||||
class DeptViewSet(CustomModelViewSet):
|
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
|
serializer_class = DeptSerializer
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
||||||
filterset_fields = ['status', 'pid']
|
filterset_fields = ['status', 'pid']
|
||||||
@@ -78,8 +81,39 @@ class DeptViewSet(CustomModelViewSet):
|
|||||||
|
|
||||||
@action(detail=False, methods=['get'])
|
@action(detail=False, methods=['get'])
|
||||||
def tree(self, request):
|
def tree(self, request):
|
||||||
"""获取部门树形结构"""
|
"""一次性查出所有部门,构建树形结构"""
|
||||||
queryset = self.get_queryset().filter(pid__isnull=True)
|
all_depts = Dept.objects.all().order_by('sort', 'id')
|
||||||
serializer = self.get_serializer(queryset, many=True)
|
dept_dict = {}
|
||||||
return Response(serializer.data)
|
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,
|
||||||
|
)
|
||||||
@@ -91,7 +91,7 @@ class MenuMetaViewSet(viewsets.ModelViewSet):
|
|||||||
|
|
||||||
class MenuViewSet(CustomModelViewSet):
|
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
|
serializer_class = MenuSerializer
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
||||||
filterset_fields = ['status', 'type', 'pid', 'name']
|
filterset_fields = ['status', 'type', 'pid', 'name']
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class RoleFilter(filters.FilterSet):
|
|||||||
|
|
||||||
class RoleViewSet(CustomModelViewSet):
|
class RoleViewSet(CustomModelViewSet):
|
||||||
"""角色管理视图集"""
|
"""角色管理视图集"""
|
||||||
queryset = Role.objects.all()
|
queryset = Role.objects.all().prefetch_related('permissions')
|
||||||
serializer_class = RoleSerializer
|
serializer_class = RoleSerializer
|
||||||
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
||||||
filterset_class = RoleFilter
|
filterset_class = RoleFilter
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from django.db.models import Prefetch, F
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework.authtoken.models import Token
|
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
|
serializer_class = UserSerializer
|
||||||
read_only_fields = ['id', 'create_time', 'update_time', 'login_ip']
|
read_only_fields = ['id', 'create_time', 'update_time', 'login_ip']
|
||||||
filterset_class = UserFilter
|
filterset_class = UserFilter
|
||||||
|
|||||||
19
backend/utils/filters_logs.py
Normal file
19
backend/utils/filters_logs.py
Normal 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
|
||||||
@@ -53,4 +53,11 @@ async function deleteDept(id: string) {
|
|||||||
return requestClient.delete(`/system/dept/${id}/`);
|
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 };
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { Plus } from '@vben/icons';
|
|||||||
import { Button, message } from 'ant-design-vue';
|
import { Button, message } from 'ant-design-vue';
|
||||||
|
|
||||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
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 { $t } from '#/locales';
|
||||||
|
|
||||||
import { useColumns } from './data';
|
import { useColumns } from './data';
|
||||||
@@ -108,7 +108,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
proxyConfig: {
|
proxyConfig: {
|
||||||
ajax: {
|
ajax: {
|
||||||
query: async (_params) => {
|
query: async (_params) => {
|
||||||
return await getDeptList();
|
return await treeDept();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user