Added some tests to modeltests/many_to_one that demonstrate a post-queryset-refactor...
[fdr-django.git] / django / utils / datastructures.py
blob4c278c0d8e98f00e5901407a657c2617be1875b2
1 class MergeDict(object):
2 """
3 A simple class for creating new "virtual" dictionaries that actually look
4 up values in more than one dictionary, passed in the constructor.
6 If a key appears in more than one of the given dictionaries, only the
7 first occurrence will be used.
8 """
9 def __init__(self, *dicts):
10 self.dicts = dicts
12 def __getitem__(self, key):
13 for dict_ in self.dicts:
14 try:
15 return dict_[key]
16 except KeyError:
17 pass
18 raise KeyError
20 def __copy__(self):
21 return self.__class__(*self.dicts)
23 def get(self, key, default=None):
24 try:
25 return self[key]
26 except KeyError:
27 return default
29 def getlist(self, key):
30 for dict_ in self.dicts:
31 if key in dict_.keys():
32 return dict_.getlist(key)
33 return []
35 def items(self):
36 item_list = []
37 for dict_ in self.dicts:
38 item_list.extend(dict_.items())
39 return item_list
41 def has_key(self, key):
42 for dict_ in self.dicts:
43 if key in dict_:
44 return True
45 return False
47 __contains__ = has_key
49 def copy(self):
50 """Returns a copy of this object."""
51 return self.__copy__()
53 class SortedDict(dict):
54 """
55 A dictionary that keeps its keys in the order in which they're inserted.
56 """
57 def __init__(self, data=None):
58 if data is None:
59 data = {}
60 super(SortedDict, self).__init__(data)
61 if isinstance(data, dict):
62 self.keyOrder = data.keys()
63 else:
64 self.keyOrder = []
65 for key, value in data:
66 if key not in self.keyOrder:
67 self.keyOrder.append(key)
69 def __deepcopy__(self, memo):
70 from copy import deepcopy
71 return self.__class__([(key, deepcopy(value, memo))
72 for key, value in self.iteritems()])
74 def __setitem__(self, key, value):
75 super(SortedDict, self).__setitem__(key, value)
76 if key not in self.keyOrder:
77 self.keyOrder.append(key)
79 def __delitem__(self, key):
80 super(SortedDict, self).__delitem__(key)
81 self.keyOrder.remove(key)
83 def __iter__(self):
84 for k in self.keyOrder:
85 yield k
87 def pop(self, k, *args):
88 result = super(SortedDict, self).pop(k, *args)
89 try:
90 self.keyOrder.remove(k)
91 except ValueError:
92 # Key wasn't in the dictionary in the first place. No problem.
93 pass
94 return result
96 def popitem(self):
97 result = super(SortedDict, self).popitem()
98 self.keyOrder.remove(result[0])
99 return result
101 def items(self):
102 return zip(self.keyOrder, self.values())
104 def iteritems(self):
105 for key in self.keyOrder:
106 yield key, super(SortedDict, self).__getitem__(key)
108 def keys(self):
109 return self.keyOrder[:]
111 def iterkeys(self):
112 return iter(self.keyOrder)
114 def values(self):
115 return [super(SortedDict, self).__getitem__(k) for k in self.keyOrder]
117 def itervalues(self):
118 for key in self.keyOrder:
119 yield super(SortedDict, self).__getitem__(key)
121 def update(self, dict_):
122 for k, v in dict_.items():
123 self.__setitem__(k, v)
125 def setdefault(self, key, default):
126 if key not in self.keyOrder:
127 self.keyOrder.append(key)
128 return super(SortedDict, self).setdefault(key, default)
130 def value_for_index(self, index):
131 """Returns the value of the item at the given zero-based index."""
132 return self[self.keyOrder[index]]
134 def insert(self, index, key, value):
135 """Inserts the key, value pair before the item with the given index."""
136 if key in self.keyOrder:
137 n = self.keyOrder.index(key)
138 del self.keyOrder[n]
139 if n < index:
140 index -= 1
141 self.keyOrder.insert(index, key)
142 super(SortedDict, self).__setitem__(key, value)
144 def copy(self):
145 """Returns a copy of this object."""
146 # This way of initializing the copy means it works for subclasses, too.
147 obj = self.__class__(self)
148 obj.keyOrder = self.keyOrder[:]
149 return obj
151 def __repr__(self):
153 Replaces the normal dict.__repr__ with a version that returns the keys
154 in their sorted order.
156 return '{%s}' % ', '.join(['%r: %r' % (k, v) for k, v in self.items()])
158 def clear(self):
159 super(SortedDict, self).clear()
160 self.keyOrder = []
162 class MultiValueDictKeyError(KeyError):
163 pass
165 class MultiValueDict(dict):
167 A subclass of dictionary customized to handle multiple values for the
168 same key.
170 >>> d = MultiValueDict({'name': ['Adrian', 'Simon'], 'position': ['Developer']})
171 >>> d['name']
172 'Simon'
173 >>> d.getlist('name')
174 ['Adrian', 'Simon']
175 >>> d.get('lastname', 'nonexistent')
176 'nonexistent'
177 >>> d.setlist('lastname', ['Holovaty', 'Willison'])
179 This class exists to solve the irritating problem raised by cgi.parse_qs,
180 which returns a list for every key, even though most Web forms submit
181 single name-value pairs.
183 def __init__(self, key_to_list_mapping=()):
184 super(MultiValueDict, self).__init__(key_to_list_mapping)
186 def __repr__(self):
187 return "<%s: %s>" % (self.__class__.__name__,
188 super(MultiValueDict, self).__repr__())
190 def __getitem__(self, key):
192 Returns the last data value for this key, or [] if it's an empty list;
193 raises KeyError if not found.
195 try:
196 list_ = super(MultiValueDict, self).__getitem__(key)
197 except KeyError:
198 raise MultiValueDictKeyError, "Key %r not found in %r" % (key, self)
199 try:
200 return list_[-1]
201 except IndexError:
202 return []
204 def __setitem__(self, key, value):
205 super(MultiValueDict, self).__setitem__(key, [value])
207 def __copy__(self):
208 return self.__class__(super(MultiValueDict, self).items())
210 def __deepcopy__(self, memo=None):
211 import copy
212 if memo is None:
213 memo = {}
214 result = self.__class__()
215 memo[id(self)] = result
216 for key, value in dict.items(self):
217 dict.__setitem__(result, copy.deepcopy(key, memo),
218 copy.deepcopy(value, memo))
219 return result
221 def get(self, key, default=None):
223 Returns the last data value for the passed key. If key doesn't exist
224 or value is an empty list, then default is returned.
226 try:
227 val = self[key]
228 except KeyError:
229 return default
230 if val == []:
231 return default
232 return val
234 def getlist(self, key):
236 Returns the list of values for the passed key. If key doesn't exist,
237 then an empty list is returned.
239 try:
240 return super(MultiValueDict, self).__getitem__(key)
241 except KeyError:
242 return []
244 def setlist(self, key, list_):
245 super(MultiValueDict, self).__setitem__(key, list_)
247 def setdefault(self, key, default=None):
248 if key not in self:
249 self[key] = default
250 return self[key]
252 def setlistdefault(self, key, default_list=()):
253 if key not in self:
254 self.setlist(key, default_list)
255 return self.getlist(key)
257 def appendlist(self, key, value):
258 """Appends an item to the internal list associated with key."""
259 self.setlistdefault(key, [])
260 super(MultiValueDict, self).__setitem__(key, self.getlist(key) + [value])
262 def items(self):
264 Returns a list of (key, value) pairs, where value is the last item in
265 the list associated with the key.
267 return [(key, self[key]) for key in self.keys()]
269 def lists(self):
270 """Returns a list of (key, list) pairs."""
271 return super(MultiValueDict, self).items()
273 def values(self):
274 """Returns a list of the last value on every key list."""
275 return [self[key] for key in self.keys()]
277 def copy(self):
278 """Returns a copy of this object."""
279 return self.__deepcopy__()
281 def update(self, *args, **kwargs):
283 update() extends rather than replaces existing key lists.
284 Also accepts keyword args.
286 if len(args) > 1:
287 raise TypeError, "update expected at most 1 arguments, got %d" % len(args)
288 if args:
289 other_dict = args[0]
290 if isinstance(other_dict, MultiValueDict):
291 for key, value_list in other_dict.lists():
292 self.setlistdefault(key, []).extend(value_list)
293 else:
294 try:
295 for key, value in other_dict.items():
296 self.setlistdefault(key, []).append(value)
297 except TypeError:
298 raise ValueError, "MultiValueDict.update() takes either a MultiValueDict or dictionary"
299 for key, value in kwargs.iteritems():
300 self.setlistdefault(key, []).append(value)
302 class DotExpandedDict(dict):
304 A special dictionary constructor that takes a dictionary in which the keys
305 may contain dots to specify inner dictionaries. It's confusing, but this
306 example should make sense.
308 >>> d = DotExpandedDict({'person.1.firstname': ['Simon'], \
309 'person.1.lastname': ['Willison'], \
310 'person.2.firstname': ['Adrian'], \
311 'person.2.lastname': ['Holovaty']})
312 >>> d
313 {'person': {'1': {'lastname': ['Willison'], 'firstname': ['Simon']}, '2': {'lastname': ['Holovaty'], 'firstname': ['Adrian']}}}
314 >>> d['person']
315 {'1': {'lastname': ['Willison'], 'firstname': ['Simon']}, '2': {'lastname': ['Holovaty'], 'firstname': ['Adrian']}}
316 >>> d['person']['1']
317 {'lastname': ['Willison'], 'firstname': ['Simon']}
319 # Gotcha: Results are unpredictable if the dots are "uneven":
320 >>> DotExpandedDict({'c.1': 2, 'c.2': 3, 'c': 1})
321 {'c': 1}
323 def __init__(self, key_to_list_mapping):
324 for k, v in key_to_list_mapping.items():
325 current = self
326 bits = k.split('.')
327 for bit in bits[:-1]:
328 current = current.setdefault(bit, {})
329 # Now assign value to current position
330 try:
331 current[bits[-1]] = v
332 except TypeError: # Special-case if current isn't a dict.
333 current = {bits[-1]: v}
335 class FileDict(dict):
337 A dictionary used to hold uploaded file contents. The only special feature
338 here is that repr() of this object won't dump the entire contents of the
339 file to the output. A handy safeguard for a large file upload.
341 def __repr__(self):
342 if 'content' in self:
343 d = dict(self, content='<omitted>')
344 return dict.__repr__(d)
345 return dict.__repr__(self)