|
|
@@ -1,3 +1,5 @@
|
|
|
+from collections import defaultdict, abc
|
|
|
+from werkzeug.datastructures import ImmutableMultiDict
|
|
|
import functools
|
|
|
|
|
|
|
|
|
@@ -45,3 +47,97 @@ def rgetattr(obj, attr, *args):
|
|
|
return getattr(obj, attr, *args)
|
|
|
|
|
|
return functools.reduce(_getattr, (obj,) + attr)
|
|
|
+
|
|
|
+
|
|
|
+def default_to_regular(d):
|
|
|
+ if isinstance(d, defaultdict):
|
|
|
+ d = {k: default_to_regular(v) for k, v in d.items()}
|
|
|
+ return d
|
|
|
+
|
|
|
+
|
|
|
+def nested_dict_iter(nested, path=()):
|
|
|
+ for key, value in nested.items():
|
|
|
+ if isinstance(value, abc.Mapping):
|
|
|
+ yield from nested_dict_iter(value, path + (key,))
|
|
|
+ else:
|
|
|
+ yield path + (key,), value
|
|
|
+
|
|
|
+
|
|
|
+def to_formdata_item(data, prefix=''):
|
|
|
+ def get_prefixed(key):
|
|
|
+ return f'{prefix}-{key}' if prefix else key
|
|
|
+
|
|
|
+ for k, val in data.items():
|
|
|
+ if isinstance(val, list):
|
|
|
+ for n, sub_val in enumerate(val):
|
|
|
+ if isinstance(sub_val, dict):
|
|
|
+ yield from to_formdata_item(sub_val, get_prefixed(f'{k}-{n}'))
|
|
|
+ else:
|
|
|
+ yield get_prefixed(f'{k}-{n}'), sub_val
|
|
|
+ elif isinstance(val, dict):
|
|
|
+ yield from to_formdata_item(val, get_prefixed(k))
|
|
|
+ else:
|
|
|
+ yield get_prefixed(k), val
|
|
|
+
|
|
|
+
|
|
|
+def formdata_mangle(data):
|
|
|
+ return ImmutableMultiDict(to_formdata_item(data))
|
|
|
+
|
|
|
+
|
|
|
+def formdata_demangle(formdata):
|
|
|
+ def iter_by_reverse_key_length(iter):
|
|
|
+ return sorted(iter, key=lambda k: len(k[0]), reverse=True)
|
|
|
+
|
|
|
+ def is_int(value):
|
|
|
+ try:
|
|
|
+ int(value)
|
|
|
+ return True
|
|
|
+ except ValueError:
|
|
|
+ return False
|
|
|
+
|
|
|
+ if not formdata:
|
|
|
+ return None
|
|
|
+
|
|
|
+ by_tuple = {tuple(k.split('-')): val for k, val in formdata.items()}
|
|
|
+
|
|
|
+ formdata_by_tuple = defaultdict(defaultdict)
|
|
|
+ for k, val in iter_by_reverse_key_length(by_tuple.items()):
|
|
|
+ recursive_setdefault(formdata_by_tuple, val, *k)
|
|
|
+
|
|
|
+ formdata_leaf_lists = defaultdict(list)
|
|
|
+ for k, val in iter_by_reverse_key_length(nested_dict_iter(formdata_by_tuple)):
|
|
|
+ if is_int(k[-1]):
|
|
|
+ recursive_dict_operation(
|
|
|
+ formdata_leaf_lists,
|
|
|
+ lambda d, k: d.setdefault(k, defaultdict(list)),
|
|
|
+ lambda d, k: d[k].append(val),
|
|
|
+ *k[:-1]
|
|
|
+ )
|
|
|
+ else:
|
|
|
+ recursive_setdefault(formdata_leaf_lists, val, *k)
|
|
|
+
|
|
|
+ formdata_final = defaultdict(list)
|
|
|
+ nested_lists = []
|
|
|
+ for k, val in iter_by_reverse_key_length(nested_dict_iter(formdata_leaf_lists)):
|
|
|
+ for n, s_k in enumerate(reversed(k)):
|
|
|
+ if is_int(s_k):
|
|
|
+ value_key = k[:len(k) - n]
|
|
|
+ if value_key not in nested_lists:
|
|
|
+ value = recursive_get(formdata_leaf_lists, *value_key)
|
|
|
+ nested_list_key = value_key[:-1]
|
|
|
+ recursive_dict_operation(
|
|
|
+ formdata_final,
|
|
|
+ lambda d, k: d.setdefault(k, defaultdict(list)),
|
|
|
+ lambda d, k: d[k].append(default_to_regular(value)),
|
|
|
+ *nested_list_key
|
|
|
+ )
|
|
|
+ nested_lists.append(value_key)
|
|
|
+ break
|
|
|
+ else:
|
|
|
+ recursive_setdefault(
|
|
|
+ formdata_final,
|
|
|
+ val,
|
|
|
+ *k
|
|
|
+ )
|
|
|
+
|
|
|
+ return default_to_regular(formdata_final)
|