Description
I have a serializer with some read-only fields which are populated by a method on the model. This worked great. Then I made a change elsewhere that broke my method. Now my fields don't show up. Instead, I would have expected the exception to reach the browser somehow -- a 500 error from DRF or from Django core.
I discovered that DRF is swallowing the exception thrown inside my function (an AttributeError
) and silently skipping the field. This is confusing to me and I don't understand how this is useful; in fact, it seems that the logic is appropriate for a de-serialization (i.e. create or update) but not for output.
If the current behavior is desired, what's the reasoning? And if so, can we add something to the guide about it, under exception handling?
For context, here's my serializer:
class SectionSerializer(serializers.HyperlinkedModelSerializer):
route_count = serializers.IntegerField(source='get_route_count', read_only=True)
And here's the method on my model:
class Section(Versionable):
...
def get_route_count(self):
if self.route_type == Section.RT_BOULDER:
return self.boulderroute_set.count()
elif self.route_type == Section.RT_SPORT:
return self.sportroute_set.count()
(My function get_route_count
is broken because I removed the .RT_*
constants in a refactor)
And this is where my AttributeError is caught in fields.py
:
class Field(object):
...
def get_attribute(self, instance):
"""
Given the *outgoing* object instance, return the primitive value
that should be used for this field.
"""
try:
return get_attribute(instance, self.source_attrs)
except (KeyError, AttributeError) as exc:
if not self.required and self.default is empty:
raise SkipField()
msg = (
'Got {exc_type} when attempting to get a value for field '
'`{field}` on serializer `{serializer}`.\nThe serializer '
'field might be named incorrectly and not match '
'any attribute or key on the `{instance}` instance.\n'
'Original exception text was: {exc}.'.format(
exc_type=type(exc).__name__,
field=self.field_name,
serializer=self.parent.__class__.__name__,
instance=instance.__class__.__name__,
exc=exc
)
)
raise type(exc)(msg)
Thanks all!