From 5a33ff760a35e289ea35cb17a340afd7ca38cb35 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Tue, 24 Sep 2013 15:13:27 -0600 Subject: [PATCH] Fixed indexing into Choices so its useful. --- CHANGES.rst | 4 ++++ docs/utilities.rst | 7 +++++++ model_utils/choices.py | 13 ++++++++----- model_utils/tests/tests.py | 6 +++--- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 449608e..159b4b1 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,10 @@ CHANGES master (unreleased) ------------------- +* Indexing into a ``Choices`` instance now translates database representations + to human-readable choice names, rather than simply indexing into an array of + choice tuples. (Indexing into ``Choices`` was previously not documented.) + * Fix bug in `InheritanceManager` with grandchild classes on Django 1.6+; `select_subclasses('child', 'child__grandchild')` would only ever get to the child class. Thanks Keryn Knight for report and proposed fix. diff --git a/docs/utilities.rst b/docs/utilities.rst index 788dd17..9f9bb93 100644 --- a/docs/utilities.rst +++ b/docs/utilities.rst @@ -52,6 +52,13 @@ the third is the human-readable version: STATUS = Choices((0, 'draft', _('draft')), (1, 'published', _('published'))) status = models.IntegerField(choices=STATUS, default=STATUS.draft) +You can index into a ``Choices`` instance to translate a database +representation to its display name: + +.. code-block:: python + + status_display = Article.STATUS[article.status] + Option groups can also be used with ``Choices``; in that case each argument is a tuple consisting of the option group name and a list of options, where each option in the list is either a string, a two-tuple, diff --git a/model_utils/choices.py b/model_utils/choices.py index f69fc9b..bc25485 100644 --- a/model_utils/choices.py +++ b/model_utils/choices.py @@ -46,8 +46,10 @@ class Choices(object): self._triples = [] # list of choices as (db, human-readable) - can include optgroups self._doubles = [] + # dictionary mapping db representation to human-readable + self._display_map = {} # dictionary mapping Python identifier to db representation - self._mapping = {} + self._identifier_map = {} # set of db representations self._db_values = set() @@ -55,7 +57,8 @@ class Choices(object): def _store(self, triple, triple_collector, double_collector): - self._mapping[triple[1]] = triple[0] + self._identifier_map[triple[1]] = triple[0] + self._display_map[triple[0]] = triple[2] self._db_values.add(triple[0]) triple_collector.append(triple) double_collector.append((triple[0], triple[2])) @@ -104,13 +107,13 @@ class Choices(object): def __getattr__(self, attname): try: - return self._mapping[attname] + return self._identifier_map[attname] except KeyError: raise AttributeError(attname) - def __getitem__(self, index): - return self._doubles[index] + def __getitem__(self, key): + return self._display_map[key] def __add__(self, other): diff --git a/model_utils/tests/tests.py b/model_utils/tests/tests.py index e0ee5e3..8381815 100644 --- a/model_utils/tests/tests.py +++ b/model_utils/tests/tests.py @@ -200,7 +200,7 @@ class ChoicesTests(TestCase): def test_indexing(self): - self.assertEqual(self.STATUS[1], ('PUBLISHED', 'PUBLISHED')) + self.assertEqual(self.STATUS['PUBLISHED'], 'PUBLISHED') def test_iteration(self): @@ -276,7 +276,7 @@ class LabelChoicesTests(ChoicesTests): def test_indexing(self): - self.assertEqual(self.STATUS[1], ('PUBLISHED', 'is published')) + self.assertEqual(self.STATUS['PUBLISHED'], 'is published') def test_default(self): @@ -380,7 +380,7 @@ class IdentifierChoicesTests(ChoicesTests): def test_indexing(self): - self.assertEqual(self.STATUS[1], (1, 'is published')) + self.assertEqual(self.STATUS[1], 'is published') def test_getattr(self):