首先安裝swagger所用的包
pip install drf-yasg
然後再settings.py 中註冊app
介面採用的token認證,在settings.py設定認證方式
SWAGGER_SETTINGS = { 'USE_SESSION_AUTH': False, 'SECURITY_DEFINITIONS': { '身份驗證': { 'type': 'apiKey', 'in': 'header', 'name': 'Authorization' } }, }
配好後開啟的認證是這樣的
在urls.py中設定路由
from django.contrib import admin from django.urls import path, include # 使用 drf_yasg API檔案生成器 檢視和openapi from django.views.static import serve from drf_yasg.views import get_schema_view from drf_yasg import openapi # 匯入許可權控制模組 from rest_framework import permissions # 檔案檢視 schema_view = get_schema_view( # API 資訊 openapi.Info( title='介面檔案', # API檔案標題 default_version='V1', # 版本資訊 description='介面檔案', # 描述內容 # terms_of_service='https://qaq.com', # 開發團隊地址 # contact=openapi.Contact(email='https://[email protected]',url='https://qaq.com'), # 聯絡人資訊:郵件、網址 # license=openapi.License(name='qaq License'), # 證書 ), public=True, # 是否公開 # permission_classes=(permissions.AllowAny,) # 設定使用者許可權 ) urlpatterns = [ path('admin/', admin.site.urls), path('userManage/', include('userManage.urls')), path('ani/', include('ani.urls')), path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), # 互動模式 path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), # 檔案模式 ]
設定好以後開啟 http://127.0.0.1:8000/swagger/即可存取到介面檔案頁面
開啟檢視後發現,請求的引數缺少了,返回的引數和介面實際返回的引數不一樣,因為我的檢視繼承APIView做了一些自定義的請求引數和返回引數。
所以需要自定義swagger
util.py檔案,因為自動生成的採用序列化 query_serializer引數就會將一些外來鍵的source欄位也加到引數裡面,所以重新去獲取序列器的欄位。再定義一個
固定格式的基礎類別
from rest_framework import serializers from drf_yasg import openapi from rest_framework.relations import PrimaryKeyRelatedField from rest_framework.fields import ChoiceField def serializer_to_swagger(ser_model, get_req=False): ''' 將序列化欄位轉成openapi的形式 ''' if ser_model is None and get_req is True: return {}, [] elif ser_model is None and get_req is False: return {} dit = {} serializer_field_mapping = { ChoiceField: openapi.TYPE_INTEGER, PrimaryKeyRelatedField: openapi.TYPE_INTEGER, serializers.IntegerField: openapi.TYPE_INTEGER, serializers.BooleanField: openapi.TYPE_BOOLEAN, serializers.CharField: openapi.TYPE_STRING, serializers.DateField: openapi.TYPE_STRING, serializers.DateTimeField: openapi.TYPE_STRING, serializers.DecimalField: openapi.TYPE_NUMBER, serializers.DurationField: openapi.TYPE_STRING, serializers.EmailField: openapi.TYPE_STRING, serializers.ModelField: openapi.TYPE_OBJECT, serializers.FileField: openapi.TYPE_STRING, serializers.FloatField: openapi.TYPE_NUMBER, serializers.ImageField: openapi.TYPE_STRING, serializers.SlugField: openapi.TYPE_STRING, serializers.TimeField: openapi.TYPE_STRING, serializers.URLField: openapi.TYPE_STRING, serializers.UUIDField: openapi.TYPE_STRING, serializers.IPAddressField: openapi.TYPE_STRING, serializers.FilePathField: openapi.TYPE_STRING, } fields = ser_model().get_fields() if get_req: required = [] for k, v in fields.items(): description = getattr(v, 'label', '') if isinstance(v, serializers.SerializerMethodField) or getattr(v, 'source'): continue elif isinstance(v, ChoiceField): description += str(dict(getattr(v, 'choices', {}))) if getattr(v, 'required', True) is not False: required.append(k) typ = serializer_field_mapping.get(type(v), openapi.TYPE_STRING) dit[k] = openapi.Schema(description=description, type=typ) return dit, required else: for k, v in fields.items(): description = getattr(v, 'label', '') if isinstance(v, ChoiceField): description += str(dict(getattr(v, 'choices', {}))) elif isinstance(v, serializers.SerializerMethodField): continue typ = serializer_field_mapping.get(type(v), openapi.TYPE_STRING) dit[k] = openapi.Schema(description=description, type=typ) return dit class ViewSwagger(object): """ openapi檢視類 重寫請求引數和返回引數,呼叫對應的類方法即可 """ get_operation = '獲取資料' get_req_params = [] get_req_body = None get_res_data = None get_res_examples = {'json': {}} get_res_description = ' ' get_res_code = 200 post_operation = '新增資料' post_req_params = [] post_req_body = None post_res_data = None post_res_examples = {'json': {}} post_res_description = ' ' post_res_code = 200 put_operation = '修改資料' put_req_params = [] put_req_body = None put_res_data = None put_res_examples = {'json': {}} put_res_description = ' ' put_res_code = 200 delete_operation = '刪除資料' delete_req_params = [] delete_req_body = None delete_res_data = None delete_res_examples = {'json': {}} delete_res_description = ' ' delete_res_code = 200 @classmethod def req_serialize_schema(cls, serializer): return serializer_to_swagger(serializer, get_req=True) @classmethod def res_serialize_schema(cls, serializer): return serializer_to_swagger(serializer, get_req=False) @classmethod def get(cls): ret = { 'manual_parameters': cls.get_req_params, 'request_body': cls.get_req_body, 'operation_summary': cls.get_operation, 'responses': { cls.get_res_code: openapi.Response(description=cls.get_res_description, schema=cls.get_res_data, examples=cls.get_res_examples)} } return ret @classmethod def post(cls): ret = { 'manual_parameters': cls.post_req_params, 'request_body': cls.post_req_body, 'operation_summary': cls.post_operation, 'responses': { cls.post_res_code: openapi.Response(description=cls.post_res_description, schema=cls.post_res_data, examples=cls.post_res_examples)} } return ret @classmethod def put(cls): ret = { 'manual_parameters': cls.put_req_params, 'request_body': cls.put_req_body, 'operation_summary': cls.put_operation, 'responses': { cls.put_res_code: openapi.Response(description=cls.put_res_description, schema=cls.put_res_data, examples=cls.put_res_examples)} } return ret @classmethod def delete(cls): ret = { 'manual_parameters': cls.delete_req_params, 'request_body': cls.delete_req_body, 'operation_summary': cls.delete_operation, 'responses': { cls.delete_res_code: openapi.Response(description=cls.delete_res_description, schema=cls.delete_res_data, examples=cls.delete_res_examples)} } return ret
下面只要在檢視中單獨定義對應的欄位即可,gender.py 如下
import copy from django.utils.decorators import method_decorator from ani import models from ani.serializer import GenderSerializer from utils.decorators import request_decrypt, auth_token from utils.myView import MyPagination, MyView, MixinGetList, MixinPostCreateModel, MixinPutUpdateModel, \ MixinDeleteDestroyModel from drf_yasg import openapi from drf_yasg.utils import swagger_auto_schema from utils.util import ViewSwagger class APISwagger(ViewSwagger): serializer = GenderSerializer res_ser_schema = ViewSwagger.res_serialize_schema(serializer) req_ser_schema, req_ser_required_fields = ViewSwagger.req_serialize_schema(serializer) get_operation = '獲取資料' get_req_params = [ openapi.Parameter(name='name__icontains', in_=openapi.IN_QUERY, description='過濾包含該欄位', type=openapi.TYPE_STRING, required=False), ] get_req_body = None get_res_data = openapi.Schema(type=openapi.TYPE_OBJECT, properties={ 'data': openapi.Schema(description='', type=openapi.TYPE_ARRAY, items=openapi.Schema(type=openapi.TYPE_OBJECT, properties={ **res_ser_schema })), 'total': openapi.Schema(description='總數', type=openapi.TYPE_INTEGER), }) get_res_examples = {'json': {}} get_res_description = 'get返回' get_res_code = 200 post_schema = copy.copy(req_ser_schema) del post_schema['id'] post_operation = '新增資料' post_req_params = [] post_req_body = openapi.Schema( type=openapi.TYPE_OBJECT, properties={ **post_schema }, required=req_ser_required_fields ) post_res_data = openapi.Schema(type=openapi.TYPE_OBJECT, properties={ **res_ser_schema }) post_res_examples = {'json': {}} post_res_description = ' ' post_res_code = 200 put_fields = req_ser_required_fields put_fields.append('id') put_operation = '新增資料' put_req_params = [] put_req_body = openapi.Schema( type=openapi.TYPE_OBJECT, properties={ **req_ser_schema }, required=put_fields ) put_res_data = openapi.Schema(type=openapi.TYPE_OBJECT, properties={ **res_ser_schema }) put_res_examples = {'json': {}} put_res_description = ' ' put_res_code = 200 @method_decorator([swagger_auto_schema(**APISwagger.delete())], name='delete') @method_decorator([swagger_auto_schema(**APISwagger.put())], name='put') @method_decorator([swagger_auto_schema(**APISwagger.post())], name='post') @method_decorator([swagger_auto_schema(**APISwagger.get())], name='get') # @method_decorator(auth_token, name='get') class GenderView(MyView, MixinGetList, MixinPostCreateModel, MixinPutUpdateModel, MixinDeleteDestroyModel): """ get: 查詢 post: 新增 put: 修改 delete: 刪除 """ queryset = models.Gender.objects.all() serializer_class = GenderSerializer all_serializer_class = GenderSerializer filter_class = ['name__icontains'] pagination_class = MyPagination lookup_field = 'id' ordeing_field = ('-id',)
開啟的介面檔案變成
try it out 呼叫介面,得到一樣的資料
注意:如果是post請求提交表單資料的時候,那麼這個檢視只能解析 parser_classes = [MultiPartParser],自定義的欄位和get請求差不多,只需要將 _in=openapi.IN_FORM 即可
專案檔案結構