limdauto / drf_openapi

[DEPRECATED] Beautiful Django Rest Framework API documentation autogeneration through OpenAPI standard
https://drf-openapi.readthedocs.io/en/latest/
MIT License
190 stars 39 forks source link

Nested serializer fields aren't described in the request body #113

Open jar3b opened 6 years ago

jar3b commented 6 years ago

Description

If main serializer has any nested serializers except ListSerializer (it's handled correctly), fields of this sub-serializers doesn't shown on html page with api description.

What I Did

class PersonSerializer(serializers.Serializer):
    first_name = serializers.CharField()
    last_name = serializers.CharField()

class DocumentSerializer(serializers.Serializer):
    doc_type= serializers.IntegerField()
    number = serializers.CharField()

class CheckDocRequestSerializer(serializers.Serializer):
    person = PersonSerializer()
    documents = serializers.ListField(child=DocumentSerializer())

Main serializer is CheckDocRequestSerializer and in this case fields doc_type, number will be shown for DocumentSerializer but PersonSerializer will be described as object without any sub-elements.

"Request example":

{
  "person": {},
  "documents": [
    {
      "doc_type": 0,
      "number": "string"
    }
  ]
}
jar3b commented 6 years ago

I prepared code fixing (presumably) this issue. You can test it by installing pip install --upgrade git+https://github.com/jar3b/drf_openapi.git@issue_113, code is here. If this works then i can create a PR.

simgelinas commented 6 years ago

That's great news, I'll give it a try

simgelinas commented 6 years ago

Thanks for looking into this unfortunately it doesn't seem to be working for me. I used the serializer below as the request_serializer and response_serializer and got the following JSON

SERIALIZER

class DummySerializer(serializers.Serializer):
    class class1(serializers.Serializer):
        class class1subclass(serializers.Serializer):
            myNumber = serializers.FloatField()
            myChar = serializers.CharField()
            myInt = serializers.IntegerField()

        singleItem = class1subclass()
        multiItem = class1subclass(many=True)
        myNumber = serializers.FloatField()
        myChar = serializers.CharField()
        myInt = serializers.IntegerField()

    singleItem = class1()
    multiItem = class1(many=True)
    myNumber = serializers.FloatField()
    myChar = serializers.CharField()
    myInt = serializers.IntegerField()

REQUEST

{
  "myNumber": 0,
  "myChar": "string",
  "multiItem": [
    {
      "myNumber": 0,
      "myChar": "string",
      "multiItem": [],
      "myInt": 0,
      "singleItem": {}
    }
  ],
  "myInt": 0,
  "singleItem": {
    "myNumber": 0,
    "myChar": "string",
    "multiItem": [],
    "myInt": 0,
    "singleItem": {}
  }
}

RESPONSE

{
  "myNumber": 0,
  "myChar": "string",
  "multiItem": [
    {
      "myNumber": 0,
      "myChar": "string",
      "multiItem": [],
      "myInt": 0,
      "singleItem": {}
    }
  ],
  "myInt": 0,
  "singleItem": {
    "myNumber": 0,
    "myChar": "string",
    "multiItem": [
      {
        "myNumber": 0,
        "myChar": "string",
        "myInt": 0
      }
    ],
    "myInt": 0,
    "singleItem": {
      "myNumber": 0,
      "myChar": "string",
      "myInt": 0
    }
  }
}
jar3b commented 6 years ago

Unfortunately i've not tested serializers with more than one nesting level. I made an update for handling this and put it in same branch.

Some features / bugs of updated solution:

Example view and serializer:

class DummySerializer(serializers.Serializer):
    class class1(serializers.Serializer):
        class class1subclass(serializers.Serializer):
            myNumber = serializers.FloatField(help_text='a1')
            myChar = serializers.CharField(help_text='b1', required=False)
            myInt = serializers.IntegerField(help_text='c1')

        singleItem = class1subclass(help_text='single2')
        multiItem = class1subclass(many=True, help_text='multi2')
        myNumber = serializers.FloatField()
        myChar = serializers.CharField()
        myInt = serializers.IntegerField(required=False)

    singleItem = class1(help_text='single3')
    multiItem = class1(many=True, help_text='multi3')
    myNumber = serializers.FloatField(required=False)
    myChar = serializers.CharField()
    myInt = serializers.IntegerField()

class TestView(APIView):
    """
    TEST
    """
    permission_classes = (IsAuthenticated,)
    serializer_class = DummySerializer

    @view_config(response_serializer=DummySerializer, request_serializer=DummySerializer)
    def post(self, request, version, format=None):
        return Response(data={})
Request example

{
  "singleItem": {
    "singleItem": {
      "myNumber": 0,
      "myChar": "string",
      "myInt": 0
    },
    "multiItem": [
      {
        "myNumber": 0,
        "myChar": "string",
        "myInt": 0
      }
    ],
    "myNumber": 0,
    "myChar": "string",
    "myInt": 0
  },
  "multiItem": [
    {
      "singleItem": {
        "myNumber": 0,
        "myChar": "string",
        "myInt": 0
      },
      "multiItem": [
        {
          "myNumber": 0,
          "myChar": "string",
          "myInt": 0
        }
      ],
      "myNumber": 0,
      "myChar": "string",
      "myInt": 0
    }
  ],
  "myNumber": 0,
  "myChar": "string",
  "myInt": 0
}


Response example

{
  "multiItem": [
    {
      "singleItem": {
        "myNumber": 0,
        "myChar": "string",
        "myInt": 0
      },
      "multiItem": [
        {
          "myNumber": 0,
          "myChar": "string",
          "myInt": 0
        }
      ],
      "myNumber": 0,
      "myChar": "string",
      "myInt": 0
    }
  ],
  "myNumber": 0,
  "myChar": "string",
  "myInt": 0,
  "singleItem": {
    "multiItem": [
      {
        "myNumber": 0,
        "myChar": "string",
        "myInt": 0
      }
    ],
    "myNumber": 0,
    "myChar": "string",
    "myInt": 0,
    "singleItem": {
      "myNumber": 0,
      "myChar": "string",
      "myInt": 0
    }
  }
}
simgelinas commented 6 years ago

Fantastic! Works really well. Thanks you so much!

jar3b commented 6 years ago

I updated the code, new features is:

example request view barbar

request example

{
  "singleItem": {
    "myIntEnum": 1,
    "myStringEnum": "FOO",
    "myBooleanEnum": true,
    "myMixedEnum": true,
    "myFloatEnum": 1.4
  },
  "multiItem": [
    {
      "myIntEnum": 1,
      "myStringEnum": "FOO",
      "myBooleanEnum": true,
      "myMixedEnum": true,
      "myFloatEnum": 1.4
    }
  ],
  "myChar1": "stringstri",
  "myChar2": "string",
  "myDateTime": "2018-01-26T08:06:47Z",
  "myDate": "2018-01-26"
}

and serializer:

class DummySerializer2(serializers.Serializer):
    class class1(serializers.Serializer):
        myIntEnum = serializers.ChoiceField(choices=[1,2,3])
        myStringEnum = serializers.ChoiceField(choices=["FOO", "BAR"])
        myBooleanEnum = serializers.ChoiceField(choices=[True, False])
        myMixedEnum = serializers.ChoiceField(choices=[True, 2, "123"])
        myFloatEnum = serializers.ChoiceField(choices=[1.4, 2.4, 2.766])

    singleItem = class1(help_text='single item')
    multiItem = class1(many=True, help_text='multi item')
    myChar1 = serializers.CharField(min_length=10, max_length=255)
    myChar2 = serializers.CharField(max_length=255)
    myDateTime = serializers.DateTimeField()
    myDate = serializers.DateField()

i push in same branch because this feature set depends on original issue (nested field inspections)