Skip to content

SerializerMethodField defaults seem to be treated incorrectly #422

@rmelick-vida

Description

@rmelick-vida

Describe the bug
I was trying migrating my project to use drf-spectactular, and ran into an interesting exception when trying to generate the schema for the first time.

I ran

./manage.py spectacular --file schema.yml

and got an exception with the following stacktrace

  Traceback (most recent call last):
  File "/src/webserver/manage.py", line 43, in <module>
    execute_from_command_line(sys.argv)
  File "/.pyenv/versions/webserver/lib/python3.6/site-packages/django/core/management/__init__.py", line 381, in execute_from_command_line
    utility.execute()
  File "/.pyenv/versions/webserver/lib/python3.6/site-packages/django/core/management/__init__.py", line 375, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/.pyenv/versions/webserver/lib/python3.6/site-packages/django/core/management/base.py", line 323, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/.pyenv/versions/webserver/lib/python3.6/site-packages/django/core/management/base.py", line 364, in execute
    output = self.handle(*args, **options)
  File "/.pyenv/versions/webserver/lib/python3.6/site-packages/drf_spectacular/management/commands/spectacular.py", line 50, in handle
    schema = generator.get_schema(request=None, public=True)
  File "/.pyenv/versions/webserver/lib/python3.6/site-packages/drf_spectacular/generators.py", line 257, in get_schema
    paths=self.parse(request, public),
  File "/.pyenv/versions/webserver/lib/python3.6/site-packages/drf_spectacular/generators.py", line 232, in parse
    path, path_regex, path_prefix, method, self.registry
  File "/.pyenv/versions/webserver/lib/python3.6/site-packages/drf_spectacular/openapi.py", line 84, in get_operation
    operation['responses'] = self._get_response_bodies()
  File "/.pyenv/versions/webserver/lib/python3.6/site-packages/drf_spectacular/openapi.py", line 1001, in _get_response_bodies
    return {'200': self._get_response_for_code(response_serializers, '200')}
  File "/.pyenv/versions/webserver/lib/python3.6/site-packages/drf_spectacular/openapi.py", line 1046, in _get_response_for_code
    component = self.resolve_serializer(serializer, 'response')
  File "/.pyenv/versions/webserver/lib/python3.6/site-packages/drf_spectacular/openapi.py", line 1188, in resolve_serializer
    component.schema = self._map_serializer(serializer, direction)
  File "/.pyenv/versions/webserver/lib/python3.6/site-packages/drf_spectacular/openapi.py", line 696, in _map_serializer
    schema = self._map_basic_serializer(serializer, direction)
  File "/.pyenv/versions/webserver/lib/python3.6/site-packages/drf_spectacular/openapi.py", line 760, in _map_basic_serializer
    schema = self._map_serializer_field(field, direction)
  File "/.pyenv/versions/webserver/lib/python3.6/site-packages/drf_spectacular/openapi.py", line 443, in _map_serializer_field
    meta = self._get_serializer_field_meta(field)
  File "/.pyenv/versions/webserver/lib/python3.6/site-packages/drf_spectacular/openapi.py", line 739, in _get_serializer_field_meta
    default = field.to_representation(field.default)
  File "/.pyenv/versions/webserver/lib/python3.6/site-packages/rest_framework/fields.py", line 1905, in to_representation
    return method(value)
  File "/src/webserver/food/serializers.py", line 124, in get_serving_measures
    instance.serving_measures if instance.serving_measures is not None else []
AttributeError: 'list' object has no attribute 'serving_measures'

To Reproduce
Here's a stripped down version of the Serializer and Model class I was using (I removed a bunch of other fields since it's from our production codebase)

class RecognizedFoodSerializer(serializers.ModelSerializer):
    food_name = CharField(required=False)
    serving_measures = serializers.SerializerMethodField(read_only=True, default=[])

    class Meta:
        model = RecognizedFood
        fields = (
            "urn",
            "food_name",
            "serving_measures",
        )

    def get_serving_measures(self, instance):
        return (
            instance.serving_measures if instance.serving_measures is not None else []
        )

class RecognizedFood(BaseModel):
    food_name = models.CharField(max_length=255, null=False)
    serving_measures = JSONField(null=True, blank=True)

Expected behavior
It appears that when trying to generate the schema for this serving_measures field, the drf-spectacular code is passing in the default value of the field (an empty list) as the second parameter to the get_serving_measures method. The drf codebase has

if field.default is not None and field.default != empty and not callable(field.default):            
    default = field.to_representation(field.default)

If I read the DRF docs for SerializerMethodField, I believe these functions need to be written to expect the entire object to be passed in as the second argument (a RecognizedFood in my example), not just the value of that field (the empty list).

The serializer method referred to by the method_name argument should accept a single argument (in addition to self), which is the object being serialized.

Am I misunderstanding something?

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingfix confirmation pendingissue has been fixed and confirmation from issue reporter is pending

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions