DJango--DRF基础教程(全)

DRF是Django的扩展,用于实现 Restful 提供了序列化器 Serializer 、更多的视图类、Mixin 扩展类,且自带接口测试文档。

安装

pip install djangorestframework==3.12.4

配置

# settings.py
INSTALLED_APPS = [
    'rest_framework'
]

# 默认值见rest_framework.settings.py
REST_FRAMEWORK = {
    # API渲染
    # 'DEFAULT_RENDERER_CLASSES': (  # 默认响应渲染类
    #     'rest_framework.renderers.JSONRenderer',  # json渲染器
    #     'rest_framework.renderers.BrowsableAPIRenderer',  # 浏览API渲染器
    # ),

    # 分页
    # 'DEFAULT_PAGINATION_CLASS': None,
    # 'PAGE_SIZE': None,
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',  # 启用分页
    'PAGE_SIZE': 10,

    # 过滤
    # 'DEFAULT_FILTER_BACKENDS': [],
    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],

    # 版本控制
    # 'DEFAULT_VERSIONING_CLASS': 'apps.core.middleware.version_control.CustomVersioning',

    # 登录
    # 'DEFAULT_AUTHENTICATION_CLASSES': [ # 身份认证
    # 'rest_framework.authentication.SessionAuthentication',
    # 'rest_framework.authentication.BasicAuthentication'
    # ],
    # 'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
    'UNAUTHENTICATED_USER': None,  # 未认证请求的request.user。默认配置依赖django.contrib.auth,因此要禁用
    # 'UNAUTHENTICATED_TOKEN': None,

    # 权限
    # 'DEFAULT_PERMISSION_CLASSES': [
    # 'rest_framework.permissions.AllowAny', # 允许所有用户请求
    # ],

    # 限流拦截
    # 'DEFAULT_THROTTLE_CLASSES': [],
    # 'DEFAULT_THROTTLE_RATES': {
    #     'user': None,
    #     'anon': None,
    # },

    # 错误处理
    # 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
}

路由

DRF 改良了 URLConf 配置的写法,除原本的配置方式外,还支持 routers 方式
直接引入

# urls.py
from rest_framework import routers
import article.views

router = routers.DefaultRouter()
router.register(r'article', article.views.ArticleViewSet)

urlpatterns = [
    re_path(r"^api/", include(router.urls)),
]

间接引入
其中basename用于视图反向获取url

# article/router.py
from rest_framework.routers import DefaultRouter
import article.views

article_router = DefaultRouter()
article_router.register("admin_article", article.views.AdminArticleViewSet, basename="admin_article")
article_router.register("article", article.views.ArticleView, basename="article")

# urls.py
from rest_framework.routers import DefaultRouter
from article.router import article_router

router = DefaultRouter()
router.registry.extend(article_router.registry)
urlpatterns = [
    re_path(r"^api/", include(route.urls))
]

Serializer(序列化与反序列化)

序列化:把模型对象(many=True时则为QuerySet)转换成字典
反序列化:对字典进行校验,并转成对象。

serializers.Serializer

可用于任意类的序列化(不局限于Django模型类)
构造函数:init(self, instance=None, data=empty, **kwargs)
其中 instance 为实例对象,data 为字典,实例化后亦可通过这两个字段取值。
此外可支持传入其他配置参数,如:

  • many=True 则表示传入instace为查询集QuerySet
  • partial=True 表示将只传入部分字段用于校验,忽略其他字段的required=True
  • context 用于传入一个字典,供 Serializer
    类中方法通self.context[‘XXX’]取值,使用get_serializer()方法时会自动为context加入以下内容:
            'request': self.request,
            'format': self.format_kwarg,
            'view': self

字段类型

布尔

  • BooleanField()
  • NullBooleanField()

表示布尔值,也接受None为有效值。

字符串

  • CharField(max_length=None, min_length=None, allow_blank=False,
    trim_whitespace=True)对应同名model 或 models.TextField
  • EmailField(max_length=None, min_length=None, allow_blank=False)
  • RegexField(regex, max_length=None, min_length=None,
    allow_blank=False)用于验证正则是否有效
  • SlugField(maxlength=50, min_length=None, allow_blank=False)
    一个验证输入满足表达式[a-zA-Z0-9_-]+的RegexField字段。
  • URLField(max_length=200, min_length=None, allow_blank=False)
    一个验证输入满足URL格式的RegexField字段。要求使用以下格式的完全限定网址http:/// ,使django.core.validators.URLValidator进行验证。
  • UUIDField(format=’hex_verbose’)
    确保输入为有效UUID字符串的字段。该to_internal_value方法将返回一个uuid.UUID实例。
  • IPAddressField(protocol=’both’, unpack_ipv4=False, **options)

数字

  • IntegerField(max_value=None, min_value=None)
  • FloatField(max_value=None, min_value=None)
  • DecimalField(max_digits, decimal_places, coerce_to_string=None,
    max_value=None, min_value=None)

日期时间

  • DateTimeField(format=“%Y-%m-%d %H:%M:%S”, input_formats=None) format
    代表输出格式的字符串,默认值为api_settings.DATETIME_FORMAT input_formats
    用于解析日期的输入格式的字符串列表
    如果model中启动auto_now或auto_now_add则此处自动启用read_only=True。
  • DateField(format=“%Y-%m-%d %H:%M:%S”, input_formats=None)
  • TimeField(format=“%Y-%m-%d %H:%M:%S”, input_formats=None)
  • DurationField()

选择字段

  • ChoiceField(choices) 对应model中配置的choices项,用于自动生成该字段allow_blank和allow_null可用于选择结果的空处理
  • MultipleChoiceField(choices)

文件上传字段

  • FileField(max_length=None, allow_empty_file=False,
    use_url=UPLOADED_FILES_USE_URL) max_length 为文件名最大长度
  • ImageField(max_length=None, allow_empty_file=False,
    use_url=UPLOADED_FILES_USE_URL)
    需要安装Pillow包或PIL包。推荐使用Pillow包,因为PIL包已经不积极维护。

复合字段

  • ListField(child, min_length=None, max_length=None)
    一个列表,其中child用于验证列表中对象的字段实例
scores = serializers.ListField(child=serializers.IntegerField(min_value=0, max_value=100))
  • DictField(child) 一个字典,其中child用于验证字典中对象的字段实例
document = DictField(child=CharField())
  • JSONField()

关联字段

  • StringRelatedField(many=False) 返回一个调用对应关系的 str
    方法的字符串。强制read_only=True。
class AlbumSerializer(serializers.ModelSerializer):
    tracks = serializers.StringRelatedField(many=True)

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'tracks']

{
    'album_name': 'Things We Lost In The Fire',
    'artist': 'Low',
    'tracks': [
        '1: Sunflower',
        '2: Whitetail',
        '3: Dinosaur Act',
        ...
    ]
}
  • PrimaryKeyRelatedField 即SlugRelatedField(slug_field=‘主键key’)特化版
  • SlugRelatedField(required=True, read_only=False, slug_field=‘id’,
    queryset=Tenant.objects.filter(is_delete=0), error_messages={“null”:
    “请选择租户”, “required”: “缺少租户”, “does_not_exist”: “租户不存在”})通常对应model中配置的外键项 queryset:如不配置该项,则必须有read_only=True many:一对多时应配置为True,allow_null:是否允许为空

自定义方法字段

SerializerMethodField(method_name=None)
method_name - 要调用的序列化程序上的方法的名称。如果未包括,则默认为get_<field_name>。

字段通用参数

read_only 表明该字段仅用于序列化输出,默认False
write_only 表明该字段仅用于反序列化输入,默认False
required 表明该字段在反序列化时必须输入,默认True
default 反序列化时使用的默认值
allow_null 表明该字段是否允许传入None,默认False(注意即使model里null=True,此处依然默认False)
validators 该字段使用的验证器
label 用于HTML展示API页面时,显示的字段名称
help_text 用于HTML展示API页面时,显示的字段帮助提示信息
error_messages 包含错误编号与错误信息的字典

内置方法、属性

  • to_representation(self, instance) 设置全局序列化逻辑,可用于增加/修改返回值
  • get_字段名(self, attrs) 设置 SerializerMethodField 类型字段的序列化逻辑。可用于增加/修改返回值
  • validate(self, attrs) 设置全局反序列化逻辑
  • validate_字段名(self, attrs) 指定单独字段的反序列化逻辑
  • is_valid() 进行反序列化校验,返回布尔值
    可设置raise_exception=True,则校验失败时抛出serializers.ValidationError,并向前端返回HTTP
    400 Bad Request
  • validated_data 反序列化结果值,必须在执行is_valid()校验成功后,再获取该属性。
  • data model实例的序列化结果值字典
  • instance 序列化时传入的实例(没有则为None,可用于区分新增和编辑)
  • save() 调用该Serializer类中的create或update方法
    如serializer.save(create_user_id=request.user.id, level=level)
# serializers.py
from rest_framework import serializers
class ArticleAdminSerializer(serializers.Serializer):
    content = serializers.CharField(required=True, trim_whitespace=False, max_length=20, error_messages={"required": "请输入内容", "blank": "内容不能为空", "null": "内容不能为空", "max_length": "太长了,小伙子"})
    is_show_home = serializers.ChoiceField(required=False, choices=((0, '否'), (1, '是')), error_messages={"invalid_choice": "选择是否首页展示", "null": "是否首页展示不能为空"})
    key = serializers.CharField(required=False, allow_null=True, allow_blank=True)
    name_by_method = serializers.SerializerMethodField()

    def get_name_by_method(self, attrs):
        return attrs.name

    def validate_key(self, attrs):
        if attrs:
            queryset = Article.objects.filter(is_delete=0,
                                              key__iexact=attrs)
            if self.instance is not None:
                queryset = queryset.exclude(pk=self.instance.id)

            if queryset.exists():
                raise VException(500, '关键字已存在')
        return attrs

# views.py
serializer = ArticleAdminSerializer(instance, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
form_data = serializer.validated_data

serializers.ModelSerializer

继承自Serializer,允许通过Meta配置模型类,并自动生成其字段及validators,且包含默认的create()和update()实现
通过extra_kwargs可为字段增加或修改validators
也可通过显式声明覆写model中字段的validators,或新增更多字段。
通过为每个filed配置source属性可以修改其在model中的映射字段,默认即同名字段。

# serializers.py
from rest_framework import serializers
from article.models import Article

class ArticleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article  # 对应表
        # fields = ('name', 'content')  # 仅指定部分字段
        # fields = "__all__" # 所有字段
        exclude = ('id',)  # 仅忽略部分字段
        extra_kwargs = {
            'name': {'max_length': 3,
                     'error_messages': {"max_length": "长度太长啦"}
                     # 'write_only': True,
                     # 'required': True
                     },
        }
  • 注意:model中的外键不会自动生成字段和validators,需要手动设置。
# 此处CheckOffCode有外键activity
class CheckOffCodeSerializer(serializers.ModelSerializer):
    activity_id = serializers.IntegerField(required=True, error_messages={"required": "缺少活动"})

    class Meta:
        model = CheckOffCode
        fields = "__all__"

    def validate(self, attrs):
        activity_id = attrs.get("activity_id")
        if activity_id:
            if not Activity.objects.filter(is_delete=0, id=activity_id).exists():
                raise serializers.ValidationError("活动不存在")
        return attrs

serializers.HyperlinkedModelSerializer

与 ModelSerializer 相比,通过新增的url字段代替主键或外键,方便前端直接通过该 url 访问其对应资源而无需自行拼接路径。

在validate中区分新增和编辑

  • 方法一:
if self.instance is not None:
    queryset = queryset.exclude(pk=self.instance.id)
if queryset.exists():
    raise VException(500, '角色名已存在')
  • 方法二(需配合context使用):
id = self.context['view'].kwargs.get("pk", None)
name_in_db = Product.objects.filter(name=name,is_delete=0).exclude(id=id).first()
if(name_in_db):
    raise VException(500, '已存在名称和规格相同的产品')

请求与响应

DRF的各个视图类均继承自View,重写了as_view方法,并对 request 做了封装。

request

DRF做了封装(rest_framework.request.Request),提供了一些额外属性。

  • data
    返回解析之后的请求体数据字典。类似于Django中标准的request.POST和 request.FILES属性,但提供如下特性:
    包含了解析之后的文件和非文件数据
    包含了对POST、PUT、PATCH请求方式解析后的数据
    利用了REST framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据
  • query_params
    即Django标准的request.GET
  • user
    根据认证策略返回用户信息。默认认证用户为 django.contrib.auth.models.User 实例,未认证用户为django.contrib.auth.models.AnonymousUser 实例。

Response

封装了新的Response类,集成了Django的各个返回类型
Response(data, status=None, template_name=None, headers=None, content_type=None)

入参

  • data:传入字典等Python内建类型,会自动被处理
  • status:默认为200
  • template_name:渲染模板名
  • content_type:通常无需传入,自动根据前端header的accept生成

返回值属性

  • data 传入data的序列化结果
  • content 传入data的render结果
  • status_code 状态码数字
from rest_framework.response import Response
...
return Response({'data': data}, status=status.HTTP_200_OK)

可以修改默认的响应器类型

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': (  # 默认响应渲染类
        'rest_framework.renderers.JSONRenderer',  # json渲染器
        # 'rest_framework.renderers.BrowsableAPIRenderer',  # 浏览API渲染器
    ),
}

视图

  • views.APIView
    继承自View,是DRF所有视图的基类,实现了上述 request 和 Response 的封装,增加了异常捕获,并在dispatch()前对请求进行身份认证、权限检查、流量控制。

  • viewsets.GenericViewSet
    较常使用,封装了对象获取、序列化、分页等功能
    应与Mixin类配合使用,并为其提供一些封装好的属性和方法:

  • queryset:指明使用的数据查询集(如model.objects.all())

  • serializer_class:指明视图使用的序列化器

  • pagination_class:指明视图使用的分页控制器,默认采用全局配置。详见分页

  • filter_backends:指明视图所用的过滤控制器,默认采用全局配置。详见过滤

  • get_queryset:获取配置的 queryset,Mixin中会使用该方法

  • get_serializer:获取并执行配置的 serializer_class,Mixin中会使用该方法

  • get_object:获取一个model实例对象,Mixin中会使用该方法
    默认使用APIView.check_object_permissions方法检查当前对象的访问权限

  • filter_queryset:传入一个queryset,并基于 filter_backends 对其进行筛选

  • lookup_field:指定一个属性,代替默认的id作为路由中的主键被检索

  • viewsets.ModelViewSet
    即 GenericViewSet + 下述五种Mixin

mixins

mixins.CreateModelMixin(成功则返回201)
mixins.RetrieveModelMixin
mixins.UpdateModelMixin
mixins.DestroyModelMixin(成功则返回204)
mixins.ListModelMixin
需配合GenericViewSet使用,提供create(), retrieve(), update(), partial_update(), destroy(), list() 等动作方法

自定义action

  • 通过路由配置
# urls.py
urlpatterns = [
    path('test/', views.Test.as_view({'get':'details'}))
]
  • 通过装饰器配置
  • methods必传
  • detail必传,为True表示对应单一资源,则url为 url_path//
    形式。(pk对应的属性由lookup_field配置项决定)
  • url_path非必传,默认为下方方法名,且允许使用正则匹配(同urls.py规则)
  • 其他配置项可用于覆盖View类的配置
# views.py
from rest_framework.decorators import action
...
    @action(methods=['get'], detail=False, url_path='reject/(?P<table>\w+)/(?P<pk>\w+)', pagination_class=None, filter_backends=None)
    def reject(self, request, table, pk, *args, **kwargs):
        print(table, pk)
        print(self.action)  # details
        return Response("hello world")

分页

默认不启用,修改全局配置可统一启用分页功能

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',  # 使用系统自带的分页
    'PAGE_SIZE': 10, # 使用 PageNumberPagination 时必须配置该项
}

视图GenericViewSet子类中也可以单独指定或禁用分页

# views.py
from rest_framework.pagination import PageNumberPagination

class ArticleViewSet(viewsets.GenericViewSet):
    pagination_class = PageNumberPagination # 单独指定分页方式
    pagination_class = None # 禁用分页
}

启用分页后,视图支持以下方法:

  • paginate_queryset:根据配置的 pagination_class,处理传入的 queryset ,返回带有分页信息的
    queryset
  • get_paginated_response:传入带分页信息的serializer.data,返回带分页信息的Response实例,DRF自带如下几种分页,可以基于其创建子类以实现自定义分页

三种分页模式

  • PageNumberPagination 普通分页 根据入参 page 指定页数(从1开始)
  • LimitOffsetPagination 偏移分页 根据入参,从第 offset 条数据,往后返回 limit 条数据
  • CursorPagination 游标分页 可传入
    size。每次请求会返回cursor,下次请求传入cursor即可从上一次返回的位置继续向下查找

过滤排序

默认不启用,修改全局配置可统一配置过滤类,或在视图GenericViewSet子类中配置

SearchFilter

用于模糊查询 search_fields 中字段

OrderingFilter

用于对前端传入的 ordering_fields 中字段排序,或通过配置ordering元组指定默认排序

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': [],
}

# views.py
from rest_framework.filters import SearchFilter, OrderingFilter

class ArticleViewSet(viewsets.GenericViewSet):
    filter_backends = [SearchFilter, OrderingFilter]  # 配置过滤类
    search_fields = ["name", "content"]  # 配置要过滤的字段
    ordering_fields = ["id", "name"]  # 配置要排序的字段
    ordering = ("-id", "name") # 配置默认的排序字段
}

# 前端请求
/api/article/?search=你好&ordering=-id
DjangoFilterBackend

通过第三方包 django_filters 引入

  • 通过filterset_fields指定支持查询的字段(精准查询)
pip install django-filter

# settings.py
INSTALLED_APPS = [
    ...
    'django_filters',  # 需要注册应用,
]
REST_FRAMEWORK = {
    ...
    'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}

class ArticleViewSet(viewsets.GenericViewSet):
    # filter_backends = [DjangoFilterBackend]  # 配置过滤类
    filterset_fields=['name']
}

# 前端请求
/api/article/?name=你好
  • 通过filter_class自定义查询规则
# views.py
class RockRecordView(viewsets.GenericViewSet):

    filter_backends = [DjangoFilterBackend]

    filter_class = RockRecordFilter

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
# filter.py
from django_filters import rest_framework as filters
from django_filters import BaseInFilter
class RockRecordFilter(filters.FilterSet):
    wall = filters.CharFilter(lookup_expr='icontains')
    type_in = BaseInFilter(field_name='route__type', lookup_expr='in')#接收数据如1,2字符串
    audit_status = filters.NumberFilter(lookup_expr='exact') 
    start_time = filters.DateTimeFilter(field_name="created_time", lookup_expr='gte')
    end_time = filters.DateTimeFilter(field_name="created_time", lookup_expr='lte')

认证、权限、频率

DRF视图中自动执行了三大验证:认证—>权限—>频率
可通过配置修改默认的认证,及未认证请求的 request.user 和 request.auth 值

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ),
    # 'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
    'UNAUTHENTICATED_USER': None,  # 未认证请求的request.user。默认配置依赖django.contrib.auth,因此要禁用
    'UNAUTHENTICATED_TOKEN': None,

    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.AllowAny', # 允许所有用户请求
    ],
}

频率限制(默认不开启)

可根据认证情况配置:

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.UserRateThrottle', # 使用 DEFAULT_THROTTLE_RATES['user']
        'rest_framework.throttling.AnonRateThrottle', # 使用 DEFAULT_THROTTLE_RATES['anon']
    ),
    'DEFAULT_THROTTLE_RATES': {
        'user': '1000/day', # 已登录用户访问频次一天1000次
        'anon': '3/m', # 未登录用户访问频次一分钟3次
    }
}

也可以根据视图配置:

# views.py
class ContactListView(APIView):
    throttle_scope = 'contacts'
    ...

class ContactDetailView(APIView):
    throttle_scope = 'contacts'
    ...

class UploadView(APIView):
    throttle_scope = 'uploads'
    ...

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.ScopedRateThrottle',
    ),
    'DEFAULT_THROTTLE_RATES': {
        'contacts': '1000/day',
        'uploads': '20/day'
    }
}

错误处理

DRF默认的异常处理,只处理APIException及其子类的异常,其他返回None。
通常可改用自定义方法,加入log日志,并为非APIException的异常添加处理和返回值。

# settings.py
REST_FRAMEWORK = {
     # 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
     'EXCEPTION_HANDLER': 'apps.core.framework.exception.custom_exception_handler',
}


# exception.py
from rest_framework.views import exception_handler

def custom_exception_handler(exc, context):
    response = exception_handler(exc, context)
    return response

Swagger配置

参考文档: https://github.com/axnsan12/drf-yasg

pip install drf_yasg

# settings.py
INSTALLED_APPS = [
    ...
    'drf_yasg', # swagger接口生成工具
]

# SWAGGER配置
SWAGGER_SETTINGS = {
    # 'PERSIST_AUTH': True,
    # 'REFETCH_SCHEMA_WITH_AUTH': False,
    # 'REFETCH_SCHEMA_ON_LOGOUT': False,
    'USE_SESSION_AUTH': False,
    # 'SECURITY_DEFINITIONS': {
    #     'Bearer': {
    #         'type': 'apiKey',
    #         'name': 'Access-Token',
    #         'in': 'header'
    #     },
    # }
}

# urls.py
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
from rest_framework.permissions import AllowAny

schema_view = get_schema_view(
    openapi.Info(
        title="我的Swagger",
        default_version='v1.0.0',
        description="Test Description",
        terms_of_service="https://github.com/TwinkleLee",
        contact=openapi.Contact(email="624061283@qq.com"),
        license=openapi.License(name="BSD License"),
    ),
    public=True,
    permission_classes=(AllowAny,),
    authentication_classes=()
)

urlpatterns = [
    re_path(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
    re_path(r'^swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
    re_path(r'^redoc/$', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
]

默认根据GenericViewSet类下的serializer_class等为接口生成Swagger,也可通过@swagger_auto_schema装饰器装饰视图方法,以自定义文档参数:

    @swagger_auto_schema(
        operation_description="后管操作信息",
        manual_parameters=[openapi.Parameter(name='group', in_=openapi.IN_QUERY, description="group", required=True,
                                             type=openapi.TYPE_STRING)],
        request_body=openapi.Schema(
            type=openapi.TYPE_OBJECT,
            required=['name', 'value'],
            properties={
                'name': openapi.Schema(type=openapi.TYPE_STRING, description='名称'),
                'value': openapi.Schema(type=openapi.TYPE_NUMBER, description='值'),
            },
        ),
        responses={200: openapi.Response('description', serializer_class)},
        responses={200: "自定义描述"},

        security=[],#表示该请求无需token验证

        tags=['operate_log'],
    )

正式环境访问/swagger时静态文件404问题
访问/swagger时页面的静态文件(js、css)是drf_yasg/static目录下的,当django debug=True 时,会自动映射到项目static下,但是当debug=False时,需要手动进行映射操作:

1.执行python manage.py collectstatic,自动搜索INSTALLED_APPS 的app的静态文件,并将其复制到项目的STATIC_ROOT目录下(通常即static目录)
2.在urls.py的urlpatterns中,添加url(r’^static/(?P .*)$', serve, {‘document_root’: STATIC_ROOT})

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值