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)

но при наследовании от этого используются родительские значения, из-за того, как работают декораторы. Я думал о написании пользовательского декоратора + пользовательского представления, но тогда это становится слишком сложным только для того, чтобы упростить представление приложения... Есть ли какой-нибудь лучший/умный способ решить это?

Вернуться на верх