SerializerMethodField defaults seem to be treated incorrectly
See original GitHub issueDescribe 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:
- Created 2 years ago
- Comments:11 (5 by maintainers)
Top GitHub Comments
Hi @tfranzel confirmed, the fixes work great! Thanks for the help 😃 .
closing this issue for now. feel free to comment if anything is missing or not working and we will follow-up.