Skip to content
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

Better handle conflicting keys in set_values #7671

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion rest_framework/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,14 @@ def set_value(dictionary, keys, value):
for key in keys[:-1]:
if key not in dictionary:
dictionary[key] = {}
elif type(dictionary[key]) is not dict:
dictionary[key] = {'': 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):
Expand Down
64 changes: 63 additions & 1 deletion tests/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import rest_framework
from rest_framework import exceptions, serializers
from rest_framework.fields import (
BuiltinSignatureError, DjangoImageField, is_simple_callable
BuiltinSignatureError, DjangoImageField, is_simple_callable, set_value
)

# Tests for helper functions.
Expand Down Expand Up @@ -2380,3 +2380,65 @@ def validate(self, obj):
),
]
}


# 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}