Last active
August 12, 2021 12:13
-
-
Save awbacker/ed0b29df769ccd0f886a to your computer and use it in GitHub Desktop.
Flattens a nested dictionary so that it can be more easily posted as multipart/form-data. Helpful especially with unit tests, requests.post, etc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def flatten_dict_for_formdata(input_dict, array_separator="[{i}]"): | |
""" | |
Recursively flattens nested dict()s into a single level suitable for passing to a library | |
that makes multipart/form-data posts. | |
- nested dicts, tuples (treated like arrays), lists, list of objects, etc | |
{ | |
"owner": { | |
"name": {"first": "andy"}, | |
"favorite_color": ("blue", "green") | |
}, | |
"superpower": "talks to snails", | |
"funiture": [ | |
{ | |
"id": "table", | |
"subparts": [ | |
{ | |
"type": "leg", | |
"qty": 7, | |
"parts": ["wood", "metal", "screws"] | |
} | |
] | |
} | |
] | |
} | |
--------------------------------------------------------- | |
"owner.favorite_color[0]": "blue", | |
"owner.favorite_color[1]": "green", | |
"owner.name.first": "andy", | |
"funiture[0]subparts[0]parts[0]": "wood", | |
"funiture[0]subparts[0]parts[1]": "metal", | |
"funiture[0]subparts[0]parts[2]": "screws", | |
"funiture[0]subparts[0]type": "leg", | |
"funiture[0]subparts[0]qty": 7, | |
"funiture[0]id": "table", | |
"superpower": "talks to snails" | |
""" | |
def __flatten(value, prefix, result_dict, previous=None): | |
""" | |
Nested function to prevent the main interface from having to have "optional" paramters | |
that must always be null on first call (e.g. prefix & result_dict). | |
""" | |
if isinstance(value, dict): | |
# If we just processed a dict, then separate the the new members with a dot. | |
# We don't want to do this if it is an object inside an array. in that case there | |
# is no separator, the [x] _is_ the separator, adding a "." like list[1].name will break | |
# but list[x]name is correct (at least for DRF/django decoding) | |
if previous == "dict": | |
prefix += "." | |
for key, v in value.iteritems(): | |
__flatten( | |
value=v, | |
prefix=prefix + key, | |
result_dict=result_dict, | |
previous="dict" | |
) | |
elif isinstance(value, (list, tuple)): | |
for i, v in enumerate(value): | |
__flatten( | |
value=v, | |
prefix=prefix + array_separator.format(i=i), # e.g. name[1] | |
result_dict=result_dict, | |
previous="array" | |
) | |
else: | |
result_dict[prefix] = value | |
return result_dict | |
# invoke the inner recursive function | |
return __flatten(input_dict, '', {}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment