调整为http相应状态

This commit is contained in:
caoqianming 2022-04-06 08:47:11 +08:00
parent d748a8dd59
commit 746c8874c4
16 changed files with 104 additions and 137 deletions

2
apps/auth1/errors.py Normal file
View File

@ -0,0 +1,2 @@
NAME_OR_PASSWORD_WRONG = '账户名或密码错误'

View File

@ -1,4 +1,5 @@
from rest_framework.exceptions import ParseError, ValidationError
from django.shortcuts import render from django.shortcuts import render
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.response import Response from rest_framework.response import Response
@ -7,8 +8,9 @@ from django.contrib.auth import authenticate, login, logout
from rest_framework.generics import CreateAPIView from rest_framework.generics import CreateAPIView
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from apps.auth1.serializers import LoginSerializer from apps.auth1.serializers import LoginSerializer
from apps.utils.response import FailResponse, SuccessResponse
# Create your views here. # Create your views here.
class TokenBlackView(APIView): class TokenBlackView(APIView):
@ -42,8 +44,8 @@ class LoginView(CreateAPIView):
password = vdata.get('password')) password = vdata.get('password'))
if user is not None: if user is not None:
login(request, user) login(request, user)
return SuccessResponse() return Response()
return FailResponse(msg='账户或密码错误') raise ParseError('账户名或密码错误', 'username_or_password_wrong')
class LogoutView(APIView): class LogoutView(APIView):
authentication_classes = [] authentication_classes = []
@ -57,4 +59,4 @@ class LogoutView(APIView):
退出登录 退出登录
""" """
logout(request) logout(request)
return SuccessResponse() return Response()

View File

@ -10,7 +10,7 @@ import os
from rest_framework import serializers, status from rest_framework import serializers, status
from drf_yasg import openapi from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema from drf_yasg.utils import swagger_auto_schema
from apps.utils.response import SuccessResponse from rest_framework.exceptions import NotFound
# Create your views here. # Create your views here.
@ -47,7 +47,7 @@ class ServerInfoView(APIView):
ret['disk']['total'] = round(disk.total/1024/1024/1024, 2) ret['disk']['total'] = round(disk.total/1024/1024/1024, 2)
ret['disk']['used'] = round(disk.used/1024/1024/1024, 2) ret['disk']['used'] = round(disk.used/1024/1024/1024, 2)
ret['disk']['percent'] = disk.percent ret['disk']['percent'] = disk.percent
return SuccessResponse(ret) return Response(ret)
def get_file_list(file_path): def get_file_list(file_path):
@ -101,7 +101,7 @@ class LogView(APIView):
"filepath": filepath, "filepath": filepath,
"size": round(fsize/1000, 1) "size": round(fsize/1000, 1)
}) })
return SuccessResponse(data=logs) return Response(logs)
class LogDetailView(APIView): class LogDetailView(APIView):
@ -117,4 +117,5 @@ class LogDetailView(APIView):
data = f.read() data = f.read()
return Response(data) return Response(data)
except: except:
return Response('未找到', status=status.HTTP_404_NOT_FOUND) raise NotFound('不存在该日志')

2
apps/system/errors.py Normal file
View File

@ -0,0 +1,2 @@
# 自定义的错误码
from rest_framework.exceptions import ValidationError

View File

@ -1,13 +1,10 @@
import logging
from django.conf import settings
from django.contrib.auth.hashers import check_password, make_password from django.contrib.auth.hashers import check_password, make_password
from django.db import transaction from django.db import transaction
from django_celery_beat.models import (CrontabSchedule, IntervalSchedule, from django_celery_beat.models import (CrontabSchedule, IntervalSchedule,
PeriodicTask) PeriodicTask)
from django_celery_results.models import TaskResult from django_celery_results.models import TaskResult
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ParseError, ValidationError
from rest_framework.mixins import (CreateModelMixin, DestroyModelMixin, from rest_framework.mixins import (CreateModelMixin, DestroyModelMixin,
ListModelMixin, RetrieveModelMixin) ListModelMixin, RetrieveModelMixin)
from rest_framework.parsers import (JSONParser, from rest_framework.parsers import (JSONParser,
@ -17,9 +14,9 @@ from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from apps.utils.mixins import (CustomCreateModelMixin) from apps.utils.mixins import (CustomCreateModelMixin)
from django.conf import settings
from apps.utils.permission import get_user_perms_map from apps.utils.permission import get_user_perms_map
from apps.utils.queryset import get_child_queryset2 from apps.utils.queryset import get_child_queryset2
from apps.utils.response import FailResponse, SuccessResponse
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
from server.celery import app as celery_app from server.celery import app as celery_app
from .filters import UserFilter from .filters import UserFilter
@ -32,8 +29,6 @@ from .serializers import (DeptCreateUpdateSerializer, DeptSerializer, DictCreate
UserCreateSerializer, UserListSerializer, UserPostCreateSerializer, UserCreateSerializer, UserListSerializer, UserPostCreateSerializer,
UserPostSerializer, UserUpdateSerializer) UserPostSerializer, UserUpdateSerializer)
logger = logging.getLogger('log')
# logger.info('请求成功! response_code:{}response_headers:{} # logger.info('请求成功! response_code:{}response_headers:{}
# response_body:{}'.format(response_code, response_headers, response_body[:251])) # response_body:{}'.format(response_code, response_headers, response_body[:251]))
@ -80,7 +75,7 @@ class PTaskViewSet(CustomModelViewSet):
obj = self.get_object() obj = self.get_object()
obj.enabled = False if obj.enabled else True obj.enabled = False if obj.enabled else True
obj.save() obj.save()
return SuccessResponse() return Response()
@transaction.atomic @transaction.atomic
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
@ -99,7 +94,7 @@ class PTaskViewSet(CustomModelViewSet):
**interval_, defaults=interval_) **interval_, defaults=interval_)
data['interval'] = interval.id data['interval'] = interval.id
except: except:
raise ValidationError('时间策略有误') raise ParseError('时间策略有误', 'schedule_wrong')
if timetype == 'crontab' and crontab_: if timetype == 'crontab' and crontab_:
data['interval'] = None data['interval'] = None
try: try:
@ -108,11 +103,11 @@ class PTaskViewSet(CustomModelViewSet):
**crontab_, defaults=crontab_) **crontab_, defaults=crontab_)
data['crontab'] = crontab.id data['crontab'] = crontab.id
except: except:
raise ValidationError('时间策略有误') raise ParseError('时间策略有误', 'schedule_wrong')
serializer = self.get_serializer(data=data) serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
serializer.save() serializer.save()
return SuccessResponse() return Response()
@transaction.atomic @transaction.atomic
def update(self, request, *args, **kwargs): def update(self, request, *args, **kwargs):
@ -133,7 +128,7 @@ class PTaskViewSet(CustomModelViewSet):
**interval_, defaults=interval_) **interval_, defaults=interval_)
data['interval'] = interval.id data['interval'] = interval.id
except: except:
raise ValidationError('时间策略有误') raise ParseError('时间策略有误', 'schedule_wrong')
if timetype == 'crontab' and crontab_: if timetype == 'crontab' and crontab_:
data['interval'] = None data['interval'] = None
try: try:
@ -144,12 +139,12 @@ class PTaskViewSet(CustomModelViewSet):
**crontab_, defaults=crontab_) **crontab_, defaults=crontab_)
data['crontab'] = crontab.id data['crontab'] = crontab.id
except: except:
raise ValidationError('时间策略有误') raise ParseError('时间策略有误', 'schedule_wrong')
instance = self.get_object() instance = self.get_object()
serializer = self.get_serializer(instance, data=data) serializer = self.get_serializer(instance, data=data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
serializer.save() serializer.save()
return SuccessResponse() return Response()
class PTaskResultViewSet(ListModelMixin, RetrieveModelMixin, CustomGenericViewSet): class PTaskResultViewSet(ListModelMixin, RetrieveModelMixin, CustomGenericViewSet):
@ -310,7 +305,7 @@ class UserViewSet(CustomModelViewSet):
serializer = self.get_serializer(data=request.data) serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
serializer.save(password=password) serializer.save(password=password)
return SuccessResponse(data=serializer.data) return Response(data=serializer.data)
@action(methods=['put'], detail=False, permission_classes=[IsAuthenticated]) @action(methods=['put'], detail=False, permission_classes=[IsAuthenticated])
def password(self, request, pk=None): def password(self, request, pk=None):
@ -326,11 +321,11 @@ class UserViewSet(CustomModelViewSet):
if new_password1 == new_password2: if new_password1 == new_password2:
user.set_password(new_password2) user.set_password(new_password2)
user.save() user.save()
return SuccessResponse() return Response()
else: else:
return FailResponse(msg='新密码两次输入不一致!') raise ParseError('新密码两次输入不一致!', 'password_not_same')
else: else:
return FailResponse(msg='旧密码错误!') raise ValidationError('旧密码错误!', 'old_password_wrong')
@action(methods=['get'], detail=False, permission_classes=[IsAuthenticated]) @action(methods=['get'], detail=False, permission_classes=[IsAuthenticated])
def info(self, request, pk=None): def info(self, request, pk=None):
@ -348,7 +343,7 @@ class UserViewSet(CustomModelViewSet):
'avatar': user.avatar, 'avatar': user.avatar,
'perms': perms, 'perms': perms,
} }
return SuccessResponse(data) return Response(data)
class FileViewSet(CustomCreateModelMixin, RetrieveModelMixin, ListModelMixin, CustomGenericViewSet): class FileViewSet(CustomCreateModelMixin, RetrieveModelMixin, ListModelMixin, CustomGenericViewSet):

View File

@ -1,4 +1,3 @@
from django.shortcuts import render
from apps.utils.dahua import dhClient from apps.utils.dahua import dhClient
from apps.utils.xunxi import xxClient from apps.utils.xunxi import xxClient
from rest_framework.response import Response from rest_framework.response import Response

46
apps/utils/exceptions.py Normal file
View File

@ -0,0 +1,46 @@
from typing import Tuple
from django.core.exceptions import PermissionDenied
from django.http import Http404
from server.settings import myLogger
from rest_framework.response import Response
from rest_framework import exceptions
from rest_framework.views import set_rollback
from django.utils.translation import gettext_lazy as _
import traceback
class MyError(exceptions.ParseError):
"""自定义业务异常
"""
def __init__(self, error:Tuple[str, str]=(), detail=None, code=None):
if error:
code, detail = error
super().__init__(detail, code)
def custom_exception_hander(exc, context):
"""
自定义异常处理
"""
if isinstance(exc, Http404):
exc = exceptions.NotFound()
elif isinstance(exc, PermissionDenied):
exc = exceptions.PermissionDenied()
if isinstance(exc, exceptions.APIException):
headers = {}
if getattr(exc, 'auth_header', None):
headers['WWW-Authenticate'] = exc.auth_header
if getattr(exc, 'wait', None):
headers['Retry-After'] = '%d' % exc.wait
if isinstance(exc.detail, (list, dict)):
data = {'err_detail':exc.detail}
data['err_codes'] = exc.get_codes()
else:
data = {'err_msg': exc.detail, 'err_code':exc.get_codes()}
set_rollback()
return Response(data, status=exc.status_code, headers=headers)
# 未处理的异常记录日志
myLogger.error(traceback.format_exc())
return Response(data={'err_code':'server_error', 'err_msg':'服务器错误'}, status=500)

View File

@ -1,8 +1,9 @@
from django.db import models
import django.utils.timezone as timezone import django.utils.timezone as timezone
from django.db import models
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from apps.utils.snowflake import worker from apps.utils.snowflake import idWorker
# 自定义软删除查询基类 # 自定义软删除查询基类
@ -55,7 +56,7 @@ class BaseModel(models.Model):
""" """
基本表 基本表
""" """
id = models.CharField(max_length=20, primary_key=True, default=worker.get_id, editable=False, verbose_name='主键ID', help_text='主键ID') id = models.CharField(max_length=20, primary_key=True, default=idWorker.get_id, editable=False, verbose_name='主键ID', help_text='主键ID')
create_time = models.DateTimeField( create_time = models.DateTimeField(
default=timezone.now, verbose_name='创建时间', help_text='创建时间') default=timezone.now, verbose_name='创建时间', help_text='创建时间')
update_time = models.DateTimeField( update_time = models.DateTimeField(

View File

@ -8,7 +8,7 @@ from django.db.models.query import QuerySet
def get_user_perms_map(user): def get_user_perms_map(user):
""" """
获取权限列表,可用redis存取 获取权限字典,可用redis存取
""" """
user_perms_map = {} user_perms_map = {}
if user.is_superuser: if user.is_superuser:
@ -44,7 +44,7 @@ class RbacPermission(BasePermission):
:return: :return:
""" """
if not hasattr(view, 'perms_map'): if not hasattr(view, 'perms_map'):
return False return True
user_perms_map = cache.get('perms_' + request.user.id, None) user_perms_map = cache.get('perms_' + request.user.id, None)
if user_perms_map is None: if user_perms_map is None:
user_perms_map = get_user_perms_map(request.user) user_perms_map = get_user_perms_map(request.user)

View File

@ -1,83 +0,0 @@
import traceback
from rest_framework.renderers import JSONRenderer
from rest_framework.views import exception_handler
from rest_framework.response import Response
from rest_framework import status as drf_status
import logging
logger = logging.getLogger('log')
def custome_exception_hander(exc, context):
"""
自定义异常处理
"""
res = exception_handler(exc, context)
if res:
return res
else:
"""
日志记录
"""
logger.error(traceback.format_exc())
return None
class FitJSONRenderer(JSONRenderer):
"""
自行封装的渲染器
"""
def render(self, data, accepted_media_type=None, renderer_context=None):
"""
如果使用这个render
普通的response将会被包装成
{"code":200000,"data":"X","msg":"X"}
这样的结果
使用方法
- 全局
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': ('utils.response.FitJSONRenderer', ),
}
- 局部
class UserCountView(APIView):
renderer_classes = [FitJSONRenderer]
:param data:
:param accepted_media_type:
:param renderer_context:
:return: {"code":200,"data":"X","msg":"X"}
"""
response_body = dict(success=True, code=200000, msg='请求成功', data=None)
response = renderer_context.get("response")
status_code = response.status_code
if isinstance(data, dict) and data.keys() == response_body.keys():
response_body = data
else:
# 处理drf格式的返回数据
if status_code >= 400: # 如果http响应异常
response_body = dict(success=False, code=400000, msg='请求失败', data=None)
response_body['data'] = data # data里是详细异常信息
response_body['code'] = status_code*1000
prefix = ""
if isinstance(data, dict):
prefix = list(data.keys())[0]
data = data[prefix]
if isinstance(data, list):
data = data[0]
if prefix != 'detail':
response_body['msg'] = prefix + str(data) # 取一部分放入msg,方便前端alert
else:
response_body['data'] = data
renderer_context.get("response").status_code = 200 # 统一成200响应, 可用body里code区分业务异常
return super(FitJSONRenderer, self).render(response_body, accepted_media_type, renderer_context)
class SuccessResponse(Response):
def __init__(self, data=None, msg='请求成功', code=200000, status=None, template_name=None, headers=None, exception=False, content_type=None):
std_data = dict(success=True, code=code, msg=msg, data=data)
super().__init__(std_data, status, template_name, headers, exception, content_type)
class FailResponse(Response):
def __init__(self, msg='请求失败', data=None, code=400000, status=400, template_name=None, headers=None, exception=False, content_type=None):
std_data = dict(success=False, code=code, msg=msg, data=data)
super().__init__(std_data, status, template_name, headers, exception, content_type)

View File

@ -3,7 +3,7 @@ from rest_framework import serializers
from django_restql.mixins import DynamicFieldsMixin from django_restql.mixins import DynamicFieldsMixin
class PkSerializer(serializers.Serializer): class PkSerializer(serializers.Serializer):
pks = serializers.ListField(child=serializers.IntegerField(min_value=1), label="主键ID列表") pks = serializers.ListField(child=serializers.CharField(max_length=20), label="主键ID列表")
class GenSignatureSerializer(serializers.Serializer): class GenSignatureSerializer(serializers.Serializer):
path = serializers.CharField(label="图片地址") path = serializers.CharField(label="图片地址")

View File

@ -2,10 +2,7 @@
# https://github.com/twitter-archive/snowflake/blob/snowflake-2010/src/main/scala/com/twitter/service/snowflake/IdWorker.scala # https://github.com/twitter-archive/snowflake/blob/snowflake-2010/src/main/scala/com/twitter/service/snowflake/IdWorker.scala
import time import time
import logging from server.settings import SNOW_DATACENTER_ID, SNOW_WORKER_ID
from django.conf import settings
# logger = logging.getLogger('log')
class InvalidSystemClock(Exception): class InvalidSystemClock(Exception):
""" """
@ -76,7 +73,6 @@ class IdWorker(object):
# 时钟回拨 # 时钟回拨
if timestamp < self.last_timestamp: if timestamp < self.last_timestamp:
# logger.error('clock is moving backwards. Rejecting requests until {}'.format(self.last_timestamp))
raise InvalidSystemClock raise InvalidSystemClock
if timestamp == self.last_timestamp: if timestamp == self.last_timestamp:
@ -101,7 +97,7 @@ class IdWorker(object):
timestamp = self._gen_timestamp() timestamp = self._gen_timestamp()
return timestamp return timestamp
worker = IdWorker(settings.SNOW_DATACENTER_ID, settings.SNOW_WORKER_ID) idWorker = IdWorker(SNOW_DATACENTER_ID, SNOW_WORKER_ID)
if __name__ == '__main__': if __name__ == '__main__':
print(worker.get_id()) print(idWorker.get_id())

View File

@ -3,7 +3,8 @@ import os
import cv2 import cv2
from server.settings import BASE_DIR from server.settings import BASE_DIR
import numpy as np import numpy as np
from .response import FailResponse, SuccessResponse from rest_framework.response import Response
from rest_framework.exceptions import ParseError
from apps.utils.viewsets import CustomGenericViewSet from apps.utils.viewsets import CustomGenericViewSet
from apps.utils.mixins import CustomCreateModelMixin from apps.utils.mixins import CustomCreateModelMixin
from apps.utils.serializers import GenSignatureSerializer from apps.utils.serializers import GenSignatureSerializer
@ -42,6 +43,6 @@ class SignatureViewSet(CustomCreateModelMixin, CustomGenericViewSet):
ext = os.path.splitext(path) ext = os.path.splitext(path)
new_path = ext[0] + '.png' new_path = ext[0] + '.png'
cv2.imwrite(new_path, image) cv2.imwrite(new_path, image)
return SuccessResponse({'path': new_path.replace(BASE_DIR, '')}) return Response({'path': new_path.replace(BASE_DIR, '')})
except: except:
return FailResponse(msg='签名照处理失败,请重新上传') raise ParseError('签名照处理失败,请重新上传')

View File

@ -4,9 +4,10 @@ from rest_framework.decorators import action
from apps.utils.mixins import CustomCreateModelMixin, CustomDestoryModelMixin, CustomUpdateModelMixin, OptimizationMixin from apps.utils.mixins import CustomCreateModelMixin, CustomDestoryModelMixin, CustomUpdateModelMixin, OptimizationMixin
from apps.utils.permission import RbacDataMixin, RbacPermission from apps.utils.permission import RbacDataMixin, RbacPermission
from apps.utils.serializers import PkSerializer from apps.utils.serializers import PkSerializer
from apps.utils.response import FailResponse, SuccessResponse from rest_framework.response import Response
from rest_framework.mixins import DestroyModelMixin, RetrieveModelMixin, ListModelMixin from rest_framework.mixins import DestroyModelMixin, RetrieveModelMixin, ListModelMixin
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from rest_framework.exceptions import ValidationError
class CustomGenericViewSet(GenericViewSet): class CustomGenericViewSet(GenericViewSet):
@ -59,9 +60,9 @@ class CustomModelViewSet(OptimizationMixin, CustomCreateModelMixin
pks = request_data.get('pks',None) pks = request_data.get('pks',None)
if pks: if pks:
self.get_queryset().filter(id__in=pks).delete(update_by=request.user) self.get_queryset().filter(id__in=pks).delete(update_by=request.user)
return SuccessResponse() return Response()
else: else:
return FailResponse(msg="未获取到pks字段") raise ValidationError("未获取到pks字段")

View File

@ -13,7 +13,7 @@ https://docs.djangoproject.com/en/3.0/ref/settings/
from datetime import datetime, timedelta from datetime import datetime, timedelta
import os import os
from . import conf from . import conf
from server.conf import DATABASES, XX_ENABLED, XX_LICENCE import logging
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@ -164,10 +164,10 @@ REST_FRAMEWORK = {
'rest_framework.permissions.IsAuthenticated', 'rest_framework.permissions.IsAuthenticated',
'apps.utils.permission.RbacPermission' 'apps.utils.permission.RbacPermission'
], ],
'DEFAULT_RENDERER_CLASSES': [ # 'DEFAULT_RENDERER_CLASSES': [
'apps.utils.response.FitJSONRenderer', # 'apps.utils.response.FitJSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer' # 'rest_framework.renderers.BrowsableAPIRenderer'
], # ],
'DEFAULT_FILTER_BACKENDS': [ 'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend', 'django_filters.rest_framework.DjangoFilterBackend',
'rest_framework.filters.SearchFilter', 'rest_framework.filters.SearchFilter',
@ -179,7 +179,7 @@ REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema', 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
'UNAUTHENTICATED_USER': None, 'UNAUTHENTICATED_USER': None,
'UNAUTHENTICATED_TOKEN': None, 'UNAUTHENTICATED_TOKEN': None,
'EXCEPTION_HANDLER': 'apps.utils.response.custome_exception_hander', 'EXCEPTION_HANDLER': 'apps.utils.exceptions.custom_exception_hander',
} }
# simplejwt配置 # simplejwt配置
SIMPLE_JWT = { SIMPLE_JWT = {
@ -302,6 +302,8 @@ LOGGING = {
}, },
} }
} }
# 实例化myLogger
myLogger = logging.getLogger('log')
# 大华ICC平台 # 大华ICC平台
DAHUA_ENABLED = conf.DAHUA_ENABLED DAHUA_ENABLED = conf.DAHUA_ENABLED

View File

@ -37,6 +37,7 @@ urlpatterns = [
# django后台 # django后台
path('django/doc/', include('django.contrib.admindocs.urls')), path('django/doc/', include('django.contrib.admindocs.urls')),
path('django/', admin.site.urls), path('django/', admin.site.urls),
path('api-auth/', include('rest_framework.urls')),
# api # api
path('', include('apps.auth1.urls')), path('', include('apps.auth1.urls')),
@ -47,13 +48,14 @@ urlpatterns = [
path('', include('apps.utils.urls')), path('', include('apps.utils.urls')),
# api文档 # api文档
path('api/docs/', include_docs_urls(title="接口文档", authentication_classes=[], permission_classes=[])), path('api/docs/', include_docs_urls(title="接口文档", authentication_classes=[], permission_classes=[])),
path('api/swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), path('api/swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
path('api/redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), path('api/redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
# 前端页面入口 # 前端页面入口
path('',TemplateView.as_view(template_name="index.html")) path('',TemplateView.as_view(template_name="index.html")),
] + \ ] + \
static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + \ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + \
static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)