Skip to content
This repository has been archived by the owner on Apr 22, 2020. It is now read-only.

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

Open
jar3b opened this issue Jan 17, 2018 · 6 comments
Open

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

jar3b opened this issue Jan 17, 2018 · 6 comments

Comments

@jar3b
Copy link

jar3b commented Jan 17, 2018

  • DRF OpenAPI version: 1.3.0
  • Python version: 3.6.2
  • Operating System: Ubuntu 16.04

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
Copy link
Author

jar3b commented Jan 19, 2018

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
Copy link

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

@simgelinas
Copy link

simgelinas commented Jan 19, 2018

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
Copy link
Author

jar3b commented Jan 22, 2018

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:

  • If request method is 'PATCH', top-level properties have required=False (because of PATCH method doesn't requires all fields to be specified), but properties of nested objects (2nd and more level) have a required flag depending on what is specified in the serializer, not necessarily False.
  • Visual representation of object type ("object" string in most cases) is overwritten with the field name (for example: singleItem Required instead of object Required), i don't know whether this is a planned behavior or not.

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
Copy link

Fantastic! Works really well. Thanks you so much!

@jar3b
Copy link
Author

jar3b commented Jan 26, 2018

I updated the code, new features is:

  • support for max_length, min_length
  • support for enums (ChoiceField)
  • support format for string values (date, datetime)

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)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants