web-dev-qa-db-fra.com

Comment afficher les options des paramètres de requête dans Django REST Framework - Swagger

Cela me dérange depuis un moment maintenant.

Mon objectif ultime est d'afficher les options des paramètres de requête dans SwaggerUI et de fournir une entrée de formulaire pour chaque paramètre de requête. Similaire à la façon dont il est affiché lors de la fourniture d'un sérialiseur pour POST.

J'utilise un ensemble de vues qui hérite de GenericViewSet et j'ai essayé ce qui suit:

  • fournir filter_fields attribut
  • fournir et définir filter_backends attribuer à (filters.DjangoFilterBackend,)
  • fournir filter_class défini à l'intérieur de mon module.
  • Substituez la méthode options pour fournir [actions][GET] information

Voici un petit hic, je n'utilise aucun modèle, donc je ne pense pas que DjangoFilterBackend m'aidera vraiment. J'utilise DjangoRESTFramework pour parler à une API externe, et je récupère simplement le résultat JSON et le transmets à la couche frontale.

Voici un petit extrait modifié de mon code pour mieux expliquer mon problème:

views.py

class SomeViewSet(GenericViewSet):
    # Note that I have all of these defined, but I have tried various combinations
    filter_fields = ('query_option_1', 'query_option_2',)
    filter_backeds = (filters.DjangoFilterBackend,)
    filter_class = SomeFilter
    query_metadata = some_dict

    # This works when request is OPTIONS
    def options(self, request, *args, **kwargs):
        if self.metadata_class is None:
            return self.http_method_not_allowed(request, *args, **kwargs)
        data = self.metadata_class().determine_metadata(request, self)
        data['actions']['GET'] = self.query_metadata
        return Response(data, status=status.HTTP_200_OK)

filters.py

class SomeFilter(FilterSet):
    strict = True
    query_option_1 = Django_filters.NumberFilter(name='query_option_1')
    query_option_2 = Django_filters.NumberFilter(name='query_option_2')

    class Meta:
        fields = ['query_option_1', 'query_option_2']

Merci d'avoir cherché et merci d'avance d'avoir répondu.

23
dajee

D'accord, pour ceux qui tombent sur cette question, je l'ai compris. C'est plutôt idiot, et je me sens un peu stupide de ne pas savoir, mais pour ma défense, ce n'était pas clairement documenté. Les informations n'ont pas été trouvées dans la documentation DRF, ni dans Django REST Swagger repository. Au lieu de cela, elles ont été trouvées sous Django-rest-framework-docs, qui est ce que Django REST Swagger est construit à partir de.

Pour spécifier votre paramètre de requête à afficher dans votre SwaggerUI en tant que champ de formulaire, vous commentez simplement comme suit:

def list(self):
    """
    param1 -- A first parameter
    param2 -- A second parameter
    """ 
    ...

Et Swagger analysera vos commentaires et mettra une entrée de formulaire pour param1 et param2. Ce qui suit -- sont les descriptions des paramètres.

20
dajee

Nouveau fanfaron

from rest_framework.filters import BaseFilterBackend
import coreapi

class SimpleFilterBackend(BaseFilterBackend):
    def get_schema_fields(self, view):
        return [coreapi.Field(
            name='query',
            location='query',
            required=False,
            type='string'
        )]

class MyViewSet(viewsets.ViewSet):
    filter_backends = (SimpleFilterBackend,)

    def list(self, request, *args, **kwargs):
        # print(request.GET.get('query'))  # Use the query param in your view
        return Response({'hello': 'world'}, status.HTTP_200_OK)
26
vadimchin

J'ai trouvé le rest framework swagger docs . afin que nous puissions écrire le type de paramètre (interger, char), la réponse, etc.

le tripple --- est nécessaire.

@api_view(["POST"])
def foo_view(request):
    """
    Your docs
    ---
    # YAML (must be separated by `---`)

    type:
      name:
        required: true
        type: string
      url:
        required: false
        type: url
      created_at:
        required: true
        type: string
        format: date-time

    serializer: .serializers.FooSerializer
    omit_serializer: false

    parameters_strategy: merge
    omit_parameters:
        - path
    parameters:
        - name: name
          description: Foobar long description goes here
          required: true
          type: string
          paramType: form
        - name: other_foo
          paramType: query
        - name: other_bar
          paramType: query
        - name: avatar
          type: file

    responseMessages:
        - code: 401
          message: Not authenticated
    """

Que diriez-vous de la situation que nous utilisons la classe mixins comme ModelViewSets. Avons-nous besoin de définir la fonction list juste pour ajouter les documents? -- Non

On peut faire comme ça:

class ArticleViewSet(viewsets.ModelViewSet):

    """
    Articles.
    ---
    list:    #<--- here!!
        parameters:
            - name: name
              description: article title
    get_price:
        omit_serializer: true

    """

    @list_route(methods=['get'])
    def get_price(self, request):
        pass
12
soooooot

Avis de non-responsabilité: j'utilise Django_filters, Les résultats peuvent donc varier. Django_filters Utilise le paramètre filter_fields Dans le DRF ViewSet, qui peut être différent de ne pas utiliser Django_filters.

Je me suis inspiré de ce fil et j'ai remplacé la méthode get_schema_fields() dans le backend de filtrage de la manière suivante.

settings.py

REST_FRAMEWORK = {
    ...
    'DEFAULT_FILTER_BACKENDS': ('location.of.custom_backend.CustomDjangoFilterBackend')
    ...
}

custom_backend.py

import coreapi
import coreschema
from Django_filters.rest_framework import DjangoFilterBackend


class CustomDjangoFilterBackend(DjangoFilterBackend):
    """
    Overrides get_schema_fields() to show filter_fields in Swagger.
    """

    def get_schema_fields(self, view):
        assert (
            coreapi is not None
        ), "coreapi must be installed to use `get_schema_fields()`"
        assert (
            coreschema is not None
        ), "coreschema must be installed to use `get_schema_fields()`"

        # append filter fields to existing fields
        fields = super().get_schema_fields(view)
        if hasattr(view, "filter_fields"):
            fields += view.filter_fields

        return [
            coreapi.Field(
                name=field,
                location='query',
                required=False,
                type='string',
            ) for field in fields
        ]
1
Sean Chon

Travailler avec openapi (et non coreapi), la façon la plus "simple" que j'ai trouvée est de ceci core dev comment :

from rest_framework.schemas.openapi import AutoSchema


class CustomSchema(AutoSchema):
    def get_operation(self, path, method):
        op = super().get_operation(path, method)
        op['parameters'].append({
            "name": "foo",
            "in": "query",
            "required": True,
            "description": "What foo does...",
            'schema': {'type': 'string'}
        })
        return op


class MyViewSet(ModelViewSet):
    schema = CustomSchema()

    def get_queryset(self):
        foo = self.request.query_params.get("foo")
        if foo:
            self.queryset = self.queryset.filter(foo=foo)
        return self.queryset
0
NiKo

Élaborer sur les réponses ci-dessus de @vadimchin - voici un exemple de travail.

# requirements.txt

djangorestframework==3.9.3
Django-rest-swagger==2.2.0
Django==2.2.1
coreapi==2.3.3

J'utilise Viewsets dans mon application. J'ai dû implémenter filter_queryset(self, request, queryset, view) comme suggéré par @jarussi.

# models.py

from Django.db import models 

class Recording(models.Model):
    _id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=511)
# serializers.py

from models import Recording
from rest_framework import serializers

class RecordingSerializer(serializers.ModelSerializer):
    class Meta:
        model = Recording
        fields = '__all__'
# views.py

from rest_framework import viewsets
from filters import NameFilterBackend
from serializers import RecordingSerializer

class RecordingViewSet(viewsets.ModelViewSet):
    serializer_class = RecordingSerializer
    queryset = Recording.objects.all()
    filter_backends = (NameFilterBackend,)
# filters.py 

from rest_framework.filters import BaseFilterBackend
import coreapi

class NameFilterBackend(BaseFilterBackend):
    def get_schema_fields(self, view):
        return [coreapi.Field(
            name='name',
            location='query',
            required=False,
            type='string',
            description='name of recording'
        )]

    def filter_queryset(self, request, queryset, view):
        try:
            n = request.query_params['name']
            queryset = queryset.filter(name=n)
        except KeyError:
            # no query parameters
            pass
        return queryset
0
user11471297

Veuillez vous référer à ce problème github qui résout le problème.

0
Devaroop