A class to encapsulate handy functionality for lists of choices
for a Django model field.
- Accepts as arguments either tuples mapping choice IDs (strings) to
- human-readable names, or simply choice IDs (in which case the ID
- is also used as the human-readable name). When iterated over,
+ Each argument to ``Choices`` is a choice, represented as either a
+ string, a two-tuple, or a three-tuple.
+ If a single string is provided, that string is used as the
+ database representation of the choice as well as the
+ human-readable presentation.
+ If a two-tuple is provided, the first item is used as the database
+ representation and the second the human-readable presentation.
+ If a triple is provided, the first item is the database
+ representation, the second a valid Python identifier that can be
+ used as a readable label in code, and the third the human-readable
+ presentation. This is most useful when the database representation
+ must sacrifice readability for some reason: to achieve a specific
+ ordering, to use an integer rather than a character field, etc.
+ Regardless of what representation of each choice is originally
+ given, when iterated over or indexed into, a ``Choices`` object
behaves as the standard Django choices list of two-tuples.
- Choice IDs can be accessed as attributes for readable code.
+ If the triple form is used, the Python identifier names can be
+ accessed as attributes on the ``Choices`` object, returning the
+ database representation. (If the single or two-tuple forms are
+ used and the database representation happens to be a valid Python
+ identifier, the database representation itself is available as an
+ attribute on the ``Choices`` object, returning itself.)
def __init__(self, *choices):
- self._choices = list(self.equalize(choices))
- self._choice_dict = dict(self._choices)
- self._reverse_dict = dict(((i, i) for i in self._choices))
+ for choice in self.equalize(choices):
+ self._choices.append((choice, choice))
+ self._choice_dict[choice] = choice
def equalize(self, choices):
if isinstance(choice, (list, tuple)):
+ yield (choice, choice, choice)
+ raise ValueError("Choices can't handle a list/tuple of length %s, only 2 or 3"
+ yield (choice, choice)
def __getattr__(self, attname):
- return self._
+ return self._e_dict[attname]
- raise AttributeError(attname)
+ raise AttributeError(attname)
def __getitem__(self, index):
return '%s(%s)' % (self.__class__.__name__,
- ', '.join(("
'%s '" % i for i in self._ choices)))
+ ', '.join(("%s" % for i in self._)))