Как я могу скрыть поля в автоматически созданных схемах или явно определить схемы?

#django #django-rest-framework #swagger #openapi #drf-yasg

Вопрос:

Я использую drf-yasg (Django REST Framework — Еще один генератор чванства) для создания документов для моего API RESTful, но он делает не совсем то, что я хочу. Я думал, что настройка read_only и write_only атрибуты to True скроют поля из документации, поскольку они отсутствуют в телах запросов и ответов, но это не так. Я не вижу никаких примеров определения схем в декораторе или даже просто скрытия поля, но если я смогу научиться делать одну из этих вещей, я буду в хорошей форме.

Давайте рассмотрим простой пример: вход пользователя.

 # serializers.py
class TokensSerializer(serializers.Serializer):
    """
    Serializes access and refresh tokens for responses to a logged-in user.
    """
    username = serializers.CharField(max_length=64, write_only=True)
    access = serializers.CharField(max_length=4096, read_only=True)
    refresh = serializers.CharField(max_length=4096, read_only=True)
    
    # ...


class LoginSerializer(serializers.ModelSerializer):
    """
    Serializes username, email, password, and tokens to allow for logging in.
    """
    class Meta():
        model = User
        fields = ['username', 'email', 'password', 'tokens']

    username = serializers.CharField(max_length=64)
    email = serializers.CharField(max_length=254, read_only=True)
    password = serializers.CharField(
        min_length=8, max_length=64, write_only=True)
    tokens = TokensSerializer(read_only=True)

    # ...
 

Эти сериализаторы генерируют Tokens и Login моделируют соответственно, которые определены в следующих форматах .json и .yaml:

 {
  "definitions": {
    "Tokens": {
      "required": ["username"],
      "type": "object",
      "properties": {
        "username": {
          "title": "Username",
          "type": "string",
          "maxLength": 64,
          "minLength": 1
        },
        "access": {
          "title": "Access",
          "type": "string",
          "readOnly": true,
          "maxLength": 4096,
          "minLength": 1
        },
        "refresh": {
          "title": "Refresh",
          "type": "string",
          "readOnly": true,
          "maxLength": 4096,
          "minLength": 1
        }
      }
    },
    "Login": {
      "required": ["username", "password"],
      "type": "object",
      "properties": {
        "username": {
          "title": "Username",
          "type": "string",
          "maxLength": 64,
          "minLength": 1
        },
        "email": {
          "title": "Email",
          "type": "string",
          "readOnly": true,
          "maxLength": 254,
          "minLength": 1
        },
        "password": {
          "title": "Password",
          "type": "string",
          "maxLength": 64,
          "minLength": 8
        },
        "tokens": {
          "$ref": "#/definitions/Tokens"
        }
      }
    }
  }
}
 
 definitions:
  Tokens:
    required:
      - username
    type: object
    properties:
      username:
        title: Username
        type: string
        maxLength: 64
        minLength: 1
      access:
        title: Access
        type: string
        readOnly: true
        maxLength: 4096
        minLength: 1
      refresh:
        title: Refresh
        type: string
        readOnly: true
        maxLength: 4096
        minLength: 1
  Login:
    required:
      - username
      - password
    type: object
    properties:
      username:
        title: Username
        type: string
        maxLength: 64
        minLength: 1
      email:
        title: Email
        type: string
        readOnly: true
        maxLength: 254
        minLength: 1
      password:
        title: Password
        type: string
        maxLength: 64
        minLength: 8
      tokens:
        $ref: '#/definitions/Tokens'
 

Однако они не подходят для каждого тела запроса или ответа, и я хотел бы либо явно определить схемы, либо использовать существующие с опущенными полями. Я понятия не имею, как реализовать последнее, но вот моя попытка первого, которая приводит к ошибке:

 class LoginView(GenericAPIView):
    """
    View for taking in an existing user's credentials and authorizing them if valid or denying access if invalid.
    """
    serializer_class = LoginSerializer

    @swagger_auto_schema(
        responses={
            201: {
                'schema': {
                    'description': 'TODO',
                    'required': ['username', 'email', 'tokens'],
                    'type': 'object',
                    'properties': {
                        'username': {
                            'title': 'Username',
                            'type': 'string',
                            'maxLength': 64,
                            'minLength': 1
                        },
                        'email': {
                            'title': 'Email',
                            'type': 'string',
                            'readOnly': True,
                            'maxLength': 254,
                            'minLength': 1
                        },
                        'tokens': {
                            '$ref': '#/definitions/Tokens'
                        }
                    }
                }
            }
        })
    def post(self, request):
      # ...
 
 Failed to load API definition.

Errors
 
Fetch error
Internal Server Error http://localhost:8000/api/v0-alpha/?format=openapi
 

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

Комментарии:

1. Я готов использовать другой генератор, если ему не хватает функций, поэтому не стесняйтесь публиковать ответы, в которых используется другой генератор. Есть какие-нибудь предложения?

Ответ №1:

Вы можете установить ref_name для мета-класса вложенного сериализатора None значение, как указано здесь: https://github.com/axnsan12/drf-yasg/issues/239#issuecomment-442629230

Комментарии:

1. Это скроет каждый экземпляр сериализатора, верно? Я хочу скрыть его только в некоторых случаях (как и другие поля) или определить схему без него.