-
-
Notifications
You must be signed in to change notification settings - Fork 6.9k
Fix default value handling for dotted sources #5375
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix default value handling for dotted sources #5375
Conversation
This looks great. It’s obviously too late for me, why does moving the guard clause fix the issue? |
On second thought, the guard is unneeded. |
44aa0e3
to
07258ca
Compare
I had originally just moved the guard, as I thought it was possible for the
Given the above, the # with guard
>>> get_attribute({'a': {'b': None}}, ['a', 'b', 'c'])
None
# without guard
>>> get_attribute({'a': {'b': None}}, ['a', 'b', 'c'])
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Users/bagel/Documents/django-rest-framework/rest_framework/fields.py", line 100, in get_attribute
instance = getattr(instance, attr)
AttributeError: 'NoneType' object has no attribute 'c' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good.
Thanks for taking the time to step through the logic!
(It gets gnarly in there. 😀)
This has caused a lot of my builds to go red as the expected keys are now missing. |
Adding |
I don't think it'll be enough in the case of nested serializers where I get an |
Hey folks. Just FYI, I'm looking at rolling a 3.7.1 this week (≈) so we can get a change/revert in here if we need to. |
I'm in the process of improving the docs and adding more tests. The below passes: def test_default_for_multiple_dotted_source(self):
class Serializer(serializers.Serializer):
c = serializers.CharField(default='x', source='a.b.c')
assert Serializer({}).data == {'c': 'x'}
assert Serializer({'a': {}}).data == {'c': 'x'}
assert Serializer({'a': None}).data == {'c': 'x'}
assert Serializer({'a': {'b': {}}}).data == {'c': 'x'}
assert Serializer({'a': {'b': None}}).data == {'c': 'x'}
assert Serializer({'a': {'b': {'c': 'abc'}}}).data == {'c': 'abc'}
def test_default_for_nested_serializer(self):
class NestedSerializer(serializers.Serializer):
a = serializers.CharField(default='1')
c = serializers.CharField(default='2', source='b.c')
class Serializer(serializers.Serializer):
nested = NestedSerializer()
assert Serializer({'nested': None}).data == {'nested': None}
assert Serializer({'nested': {}}).data == {'nested': {'a': '1', 'c': '2'}}
assert Serializer({'nested': {'a': '3', 'b': {}}}).data == {'nested': {'a': '3', 'c': '2'}}
assert Serializer({'nested': {'a': '3', 'b': {'c': '4'}}}).data == {'nested': {'a': '3', 'c': '4'}} |
yup, I failed to reproduce with regular serializers, but I'm having the issue with |
… when path components may be null. Ref encode#5375, encode#5727
… when path components may be null. Ref encode#5375, encode#5727
… when path components may be null. Ref encode#5375, encode#5727
This fixes #5371. In short, default handling works as expected when traversed attributes return an
object
/dict
, but not when one part of the traversal returnsNone
.