Django drf модель viewset самодокументирование
Надеялся создать пользовательский ModelViewSet для наследования от него, который создает обобщенную информацию о различных CRUD маршрутах. Сейчас нам приходится перезаписывать методы, которые не нуждаются в изменении. Некоторые представления требуют перезаписи почти каждого метода или написания функций представления и т.д., но другие могут иметь 1 или 0 изменений от просто ModelViewSet. При использовании django spectacular это делает модели слишком огромными, и скрывает необходимую логику между беспорядочным текстом.
Я надеюсь, что есть более умный/лучший способ сделать это?
Пример
Учитывая простую модель NameTag, которая предоставляется в тестовом api:
models.py
from django.db import models
class NameTag(models.Model):
name = models.CharField(max_length=50)
serializers.py
from rest_framework import serializers
from .models import NameTag
class NameTagSerializer(serializers.Serializer):
class Meta:
model = NameTag
api.py
from rest_framework import viewsets
from .models import NameTag
from .serializers import NameTagSerializer
class NameTagViewset(viewsets.ModelViewSet):
serializer_class = NameTagSerializer
model = NameTag
при использовании extend_schema набор представлений становится огромным.
documented_api.py
from drf_spectacular.utils import extend_schema
from rest_framework import serializers, viewsets
class NameTagViewset(viewsets.ModelViewSet):
serializer_class = NameTagSerializer
model = NameTag
@extend_schema(
operation_id='New NameTag',
description='Create a new NameTag',
tags=['NameTags']
)
def create(self, request, *args, **kwargs):
return super().create(request, *args, **kwargs)
@extend_schema(
operation_id='List of NameTags',
description='All NameTags',
tags=['NameTags'],
)
def list(self, request, *args, **kwargs):
return super().list(request, *args, **kwargs)
@extend_schema(
operation_id='NameTag details',
description='Info about current NameTags',
tags=['NameTags']
)
def retrieve(self, request, *args, **kwargs):
return super().retrieve(request, *args, **kwargs)
@extend_schema(
operation_id='Update NameTag info',
description='Update info of current NameTag',
tags=['NameTags']
)
def update(self, request, *args, **kwargs):
return super().update(request, *args, **kwargs)
@extend_schema(
operation_id='Partial update NameTag info',
description='Partial update info of current NameTag',
tags=['NameTags']
)
def partial_update(self, request, *args, **kwargs):
return super().update(request, *args, **kwargs)
@extend_schema(
operation_id='Delete NameTag',
description='Delete current NameTag',
tags=['NameTags']
)
def destroy(self, request, *args, **kwargs):
return super().destroy(request, *args, **kwargs)
Это полностью уничтожает цель использования ModelViewSet
Я решил написать класс, который предоставляет общую информацию. Это не оптимальное решение, но я сделал его просто для тестирования. Идея была в том, что вы можете даже перезаписать определенные части doc без перезаписи метода, если вы не меняете никакой логики.
Например: API_DOCUMENTATION['list']['tags'] += ['TAGS']
documented_ModelViewSet.py
from drf_spectacular.utils import extend_schema
from rest_framework import viewsets
class documented_viewset(viewsets.ModelViewSet):
API_MODEL = {'name': None, 'tags': [], 'serializer': None}
API_DOCUMENTATION = {
'list': {
'operation_id': f'List of {API_MODEL.get("name")}',
'description': f'List of {API_MODEL.get("name")} instances',
'tags': API_MODEL.get("tags")
},
'retrieve': {
'operation_id': f'{API_MODEL.get("name")} details',
'description': f'Detail info about {API_MODEL.get("name")} by id',
'tags': API_MODEL.get("tags")
},
'update': {
'operation_id': f'Update {API_MODEL.get("name")}',
'description': f'Update {API_MODEL.get("name")} with id',
'tags': API_MODEL.get("tags")
},
'partial_update': {
'operation_id': f'Partial_update {API_MODEL.get("name")}',
'description': f'Partial_update {API_MODEL.get("name")} with id',
'tags': API_MODEL.get("tags")
},
'destroy': {
'operation_id': f'Delete {API_MODEL.get("name")}',
'description': f'Delete {API_MODEL.get("name")} by id',
'tags': API_MODEL.get("tags")
},
'create': {
'operation_id': f'New {API_MODEL.get("name")}',
'description': f'Create new {API_MODEL.get("name")}',
'tags': API_MODEL.get("tags"),
}
}
if API_MODEL.get('serializer'):
API_DOCUMENTATION['create'].update(**{
'request': API_MODEL.get('serializer'),
'responses': {201: API_MODEL.get('serializer')},
})
@extend_schema(**API_DOCUMENTATION.get('list'))
def list(self, request, *args, **kwargs):
return super().list(request, *args, **kwargs)
@extend_schema(**API_DOCUMENTATION.get('retrieve'))
def retrieve(self, request, pk, *args, **kwargs):
return super().retrieve(request, pk, *args, **kwargs)
@extend_schema(**API_DOCUMENTATION.get('update'))
def update(self, request, pk, *args, **kwargs):
return super().update(request, pk, *args, **kwargs)
@extend_schema(**API_DOCUMENTATION.get('partial_update'))
def partial_update(self, request, pk, *args, **kwargs):
return super().partial_update(request, pk, *args, **kwargs)
@extend_schema(**API_DOCUMENTATION.get('destroy'))
def destroy(self, request, pk, *args, **kwargs):
return super().destroy(request, pk, *args, **kwargs)
@extend_schema(**API_DOCUMENTATION.get('create'))
def create(self, request, *args, **kwargs):
return super().create(request, *args, **kwargs)
но при наследовании от этого используются родительские значения, из-за того, как работают декораторы. Я думал о написании пользовательского декоратора + пользовательского представления, но тогда это становится слишком сложным только для того, чтобы упростить представление приложения... Есть ли какой-нибудь лучший/умный способ решить это?