Django-rest-framework: How to set a field type on SerializerMethodField for OpenAPI schema generation?

Created on 13 Jan 2020  ·  3Comments  ·  Source: encode/django-rest-framework

Consider this useless serializer:

class MySerializer(serializers.Serializer):

    random_number = serializers.SerializerMethodField()

    def get_random_number(self):
        # Chosen by fair dice roll.
        # Guaranteed to be random.
        return 4

Assuming you added this serializer in an endpoint and generated the OpenAPI schema, the serializer would look like this:

properties:
  random_number:
    type: string
  required:
  - random_number

The issue here is there is no way to tell DRF to use type: number here. I thought of a few possibilities:

  • An output_field attribute, like serializers.SerializerMethodField(output_field=serializers.IntegerField()), similarly to Django's Query Expressions
  • A decorator, like openapi_type(serializers.SerializerMethodField(), serializers.IntegerField())
  • An attribute, similarly to setting descriptions on admin actions:

    random_number = serializers.SerializerMethodField()
    
    def get_random_number(self):
      return 4
    
    get_random_number.output_field = serializers.IntegerField()
    
  • Simply something DRF does not support, which probably should be documented somewhere as a possible caveat.
Enhancement Schema Generation

Most helpful comment

For me, I think the API I'd prefer is something using type hinting like:

def get_foo(self, obj) -> int:
    return 1

It works in every Python version currently supported. This idea was used in https://github.com/encode/django-rest-framework/pull/7089 but it wasn't separated out into another PR. Looking at that implementation, it seems like it doesn't handle collections. At some point you'd be better using a nested serializer, but I think simple cases like List[int], List[str], Dict[str, int] etc. should be supported.

@carlfarrington what do you think? Is it worth trying this approach or are there problems with it? If it's worth trying, is it also worth getting something basic in that can handle basic types and ignore nested fields for now or would we want (nested or not) arrays and objects from the start?

All 3 comments

Hi @Lucidiot.

I guess the immediate way to do this would be to override AutoSchema._map_field().

You can either do that per-serializer/view to expect the field in question, or add an attribute like you say.

We'll come round to something in this way eventually, but currently I'm not sure about the best option, in terms of API.

For me, I think the API I'd prefer is something using type hinting like:

def get_foo(self, obj) -> int:
    return 1

It works in every Python version currently supported. This idea was used in https://github.com/encode/django-rest-framework/pull/7089 but it wasn't separated out into another PR. Looking at that implementation, it seems like it doesn't handle collections. At some point you'd be better using a nested serializer, but I think simple cases like List[int], List[str], Dict[str, int] etc. should be supported.

@carlfarrington what do you think? Is it worth trying this approach or are there problems with it? If it's worth trying, is it also worth getting something basic in that can handle basic types and ignore nested fields for now or would we want (nested or not) arrays and objects from the start?

We'll promote and document map_field() for 3.12. That's the right way to go here.

Was this page helpful?
0 / 5 - 0 ratings