question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

SerializerMethodField defaults seem to be treated incorrectly

See original GitHub issue

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?

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:11 (5 by maintainers)

github_iconTop GitHub Comments

2reactions
rmelick-vidacommented, Sep 28, 2021

Hi @tfranzel confirmed, the fixes work great! Thanks for the help 😃 .

0reactions
tfranzelcommented, Sep 22, 2021

closing this issue for now. feel free to comment if anything is missing or not working and we will follow-up.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Speeding up Django Rest Framework Model Serializer N+1 ...
The problem with this is obviously that for each order that gets serialized it makes an additional query to the DB which slows...
Read more >
Serializer fields - Django REST framework
By default field values are treated as mapping to an attribute on the object. If you need to customize how the field value...
Read more >
Using DRF Effectively to Build Cleaner and Faster APIs in ...
Solving slow queries by eliminating the N+1 query problem; Custom Response Format; SerializerMethodField to add read-only derived data to the response; Using ...
Read more >
drf-yasg Documentation - Read the Docs
Support for SerializerMethodField . ... info - Swagger API Info object; if omitted, defaults to DEFAULT_INFO.
Read more >
http headers (content-range) wrong for static audio files ...
For some reason this doesn't seem to have any effect on ... workaround is to serve the audio via a URL that is...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found