Better handle conflicting keys in set_values

This commit adds logic to set_values to move values on keys to be added
upon to a blank key inside the value's previous key. This would allow
constructions like:

```
fk_object = HyperlinkedRelatedField(source='fk')
fk_name = CharField('fk.field')
```

Tests are also added specifically for set_values with the examples given
in the function description as well as for this change.
This commit is contained in:
Étienne Beaulé 2020-12-24 15:35:48 -05:00
parent 8351747d98
commit 9b33778667
No known key found for this signature in database
GPG Key ID: 8787C2B2CDA9A57F
2 changed files with 69 additions and 2 deletions

View File

@ -125,9 +125,14 @@ def set_value(dictionary, keys, value):
for key in keys[:-1]: for key in keys[:-1]:
if key not in dictionary: if key not in dictionary:
dictionary[key] = {} dictionary[key] = {}
elif type(dictionary[key]) is not dict:
dictionary[key] = {'': dictionary[key]}
dictionary = dictionary[key] dictionary = dictionary[key]
dictionary[keys[-1]] = value if keys[-1] in dictionary and type(dictionary[keys[-1]]) is dict:
dictionary[keys[-1]][''] = value
else:
dictionary[keys[-1]] = value
def to_choices_dict(choices): def to_choices_dict(choices):

View File

@ -14,7 +14,7 @@ from django.utils.timezone import activate, deactivate, override, utc
import rest_framework import rest_framework
from rest_framework import exceptions, serializers from rest_framework import exceptions, serializers
from rest_framework.fields import ( from rest_framework.fields import (
BuiltinSignatureError, DjangoImageField, is_simple_callable BuiltinSignatureError, DjangoImageField, is_simple_callable, set_value
) )
# Tests for helper functions. # Tests for helper functions.
@ -2380,3 +2380,65 @@ class TestValidationErrorCode:
), ),
] ]
} }
# Tests for set_value function
# ----------------------------
class TestSetValue:
def test_no_keys(self):
"""
If no keys are provided, but a dict as value, add the dicts
"""
d = {'a': 1}
set_value(d, [], {'b': 2})
assert d == {'a': 1, 'b': 2}
def test_one_key(self):
"""
If a key + value provided, add the value to the dict with key
"""
d = {'a': 1}
set_value(d, ['x'], 2)
assert d == {'a': 1, 'x': 2}
def test_many_keys(self):
"""
With many keys, add the item to the in-most dict
"""
d = {'a': 1}
set_value(d, ['x', 'y'], 2)
assert d == {'a': 1, 'x': {'y': 2}}
def test_many_keys_existing(self):
"""
With many keys with existing in-built dict
"""
d = {'a': 1, 'x': {'a': 2}}
set_value(d, ['x', 'y'], 3)
assert d == {'a': 1, 'x': {'a': 2, 'y': 3}}
def test_conflicting_keys(self):
"""
If a value exists where a key will be added, use a blank key for old value
"""
d = {'a': 1, 'x': 2}
set_value(d, ['x', 'y'], 3)
assert d == {'a': 1, 'x': {'': 2, 'y': 3}}
def test_reverse_conflict(self):
"""
If a dict exists and a value is to be added, add it as blank key
"""
d = {'a': 1, 'x': {'y': 2}}
set_value(d, ['x'], 3)
assert d == {'a': 1, 'x': {'y': 2, '': 3}}
def test_overwrite_conflict(self):
"""
If a newer final value comes, replace with the older
"""
d = {'a': 1, 'x': 2}
set_value(d, ['x'], 3)
assert d == {'a': 1, 'x': 3}