3 A simple class for creating new "virtual" dictionaries that actualy look
4 up values in more than one dictionary, passed in the constructor.
6 def __init__(self
, *dicts
):
9 def __getitem__(self
, key
):
10 for dict in self
.dicts
:
17 def get(self
, key
, default
):
23 def getlist(self
, key
):
24 for dict in self
.dicts
:
26 return dict.getlist(key
)
33 for dict in self
.dicts
:
34 item_list
.extend(dict.items())
37 def has_key(self
, key
):
38 for dict in self
.dicts
:
43 class MultiValueDictKeyError(KeyError):
46 class MultiValueDict(dict):
48 A subclass of dictionary customized to handle multiple values for the same key.
50 >>> d = MultiValueDict({'name': ['Adrian', 'Simon'], 'position': ['Developer']})
55 >>> d.get('lastname', 'nonexistent')
57 >>> d.setlist('lastname', ['Holovaty', 'Willison'])
59 This class exists to solve the irritating problem raised by cgi.parse_qs,
60 which returns a list for every key, even though most Web forms submit
61 single name-value pairs.
63 def __init__(self
, key_to_list_mapping
=()):
64 dict.__init
__(self
, key_to_list_mapping
)
67 return "<MultiValueDict: %s>" % dict.__repr
__(self
)
69 def __getitem__(self
, key
):
71 Returns the last data value for this key, or [] if it's an empty list;
72 raises KeyError if not found.
75 list_
= dict.__getitem
__(self
, key
)
77 raise MultiValueDictKeyError
, "Key %r not found in %r" % (key
, self
)
83 def _setitem_list(self
, key
, value
):
84 dict.__setitem
__(self
, key
, [value
])
85 __setitem__
= _setitem_list
87 def get(self
, key
, default
=None):
88 "Returns the default value if the requested data doesn't exist"
97 def getlist(self
, key
):
98 "Returns an empty list if the requested data doesn't exist"
100 return dict.__getitem
__(self
, key
)
104 def setlist(self
, key
, list_
):
105 dict.__setitem
__(self
, key
, list_
)
107 def setdefault(self
, key
, default
=None):
112 def setlistdefault(self
, key
, default_list
=()):
114 self
.setlist(key
, default_list
)
115 return self
.getlist(key
)
117 def appendlist(self
, key
, value
):
118 "Appends an item to the internal list associated with key"
119 self
.setlistdefault(key
, [])
120 dict.__setitem
__(self
, key
, self
.getlist(key
) + [value
])
124 Returns a list of (key, value) pairs, where value is the last item in
125 the list associated with the key.
127 return [(key
, self
[key
]) for key
in self
.keys()]
130 "Returns a list of (key, list) pairs."
131 return dict.items(self
)
134 "Returns a list of the last value on every key list."
135 return [self
[key
] for key
in self
.keys()]
138 "Returns a copy of this object."
140 # Our custom __setitem__ must be disabled for copying machinery.
141 MultiValueDict
.__setitem
__ = dict.__setitem
__
142 cp
= copy
.deepcopy(self
)
143 MultiValueDict
.__setitem
__ = MultiValueDict
._setitem
_list
146 def update(self
, other_dict
):
147 "update() extends rather than replaces existing key lists."
148 if isinstance(other_dict
, MultiValueDict
):
149 for key
, value_list
in other_dict
.lists():
150 self
.setlistdefault(key
, []).extend(value_list
)
153 for key
, value
in other_dict
.items():
154 self
.setlistdefault(key
, []).append(value
)
156 raise ValueError, "MultiValueDict.update() takes either a MultiValueDict or dictionary"
158 class DotExpandedDict(dict):
160 A special dictionary constructor that takes a dictionary in which the keys
161 may contain dots to specify inner dictionaries. It's confusing, but this
162 example should make sense.
164 >>> d = DotExpandedDict({'person.1.firstname': ['Simon'],
165 'person.1.lastname': ['Willison'],
166 'person.2.firstname': ['Adrian'],
167 'person.2.lastname': ['Holovaty']})
169 {'person': {'1': {'lastname': ['Willison'], 'firstname': ['Simon']},
170 '2': {'lastname': ['Holovaty'], 'firstname': ['Adrian']}}}
172 {'1': {'firstname': ['Simon'], 'lastname': ['Willison'],
173 '2': {'firstname': ['Adrian'], 'lastname': ['Holovaty']}
175 {'firstname': ['Simon'], 'lastname': ['Willison']}
177 # Gotcha: Results are unpredictable if the dots are "uneven":
178 >>> DotExpandedDict({'c.1': 2, 'c.2': 3, 'c': 1})
181 def __init__(self
, key_to_list_mapping
):
182 for k
, v
in key_to_list_mapping
.items():
185 for bit
in bits
[:-1]:
186 current
= current
.setdefault(bit
, {})
187 # Now assign value to current position
189 current
[bits
[-1]] = v
190 except TypeError: # Special-case if current isn't a dict.
191 current
= {bits
[-1]: v
}