Skip to content

Instantly share code, notes, and snippets.

@awbacker
Last active August 12, 2021 12:13
Show Gist options
  • Save awbacker/ed0b29df769ccd0f886a to your computer and use it in GitHub Desktop.
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
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