Merge pull request #384 from Asday/subset

Added `Choices().subset()`.
This commit is contained in:
Asif Saif Uddin 2019-08-20 05:02:47 +06:00 committed by GitHub
commit d3534d656d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 68 additions and 2 deletions

View file

@ -1,4 +1,5 @@
| ad-m <github.com/ad-m>
| Adam Barnes <sara.and.zuka+django-model-utils@gmail.com>
| Alejandro Varas <alej0varas@gmail.com>
| Alex Orange <crazycasta@gmail.com>
| Alexey Evseev <myhappydo@gmail.com>

View file

@ -1,6 +1,10 @@
CHANGES
=======
3.3.0 (2019.08.19)
------------------
- Added `Choices.subset`.
3.2.0 (2019.06.21)
-------------------
- Catch `AttributeError` for deferred abstract fields, fixes GH-331.
@ -413,4 +417,3 @@ CHANGES
-----
* Added ``QueryManager``

View file

@ -84,6 +84,27 @@ instances and other iterable objects that could be converted into Choices:
STATUS = GENERIC_CHOICES + [(2, 'featured', _('featured'))]
status = models.IntegerField(choices=STATUS, default=STATUS.draft)
Should you wish to provide a subset of choices for a field, for
instance, you have a form class to set some model instance to a failed
state, and only wish to show the user the failed outcomes from which to
select, you can use the ``subset`` method:
.. code-block:: python
from model_utils import Choices
OUTCOMES = Choices(
(0, 'success', _('Successful')),
(1, 'user_cancelled', _('Cancelled by the user')),
(2, 'admin_cancelled', _('Cancelled by an admin')),
)
FAILED_OUTCOMES = OUTCOMES.subset('user_cancelled', 'admin_cancelled')
The ``choices`` attribute on the model field can then be set to
``FAILED_OUTCOMES``, thus allowing the subset to be defined in close
proximity to the definition of all the choices, and reused elsewhere as
required.
Field Tracker
=============

View file

@ -1,4 +1,4 @@
from .choices import Choices # noqa:F401
from .tracker import FieldTracker, ModelTracker # noqa:F401
__version__ = '3.2.0'
__version__ = '3.3.0'

View file

@ -142,3 +142,17 @@ class Choices(object):
def __deepcopy__(self, memo):
return self.__class__(*copy.deepcopy(self._triples, memo))
def subset(self, *new_identifiers):
identifiers = set(self._identifier_map.keys())
if not identifiers.issuperset(new_identifiers):
raise ValueError(
'The following identifiers are not present: %s' %
identifiers.symmetric_difference(new_identifiers),
)
return self.__class__(*[
choice for choice in self._triples
if choice[1] in new_identifiers
])

View file

@ -279,3 +279,30 @@ class IdentifierChoicesTests(ChoicesTests):
('group b', [(3, 'three')]),
],
)
class SubsetChoicesTest(TestCase):
def setUp(self):
self.choices = Choices(
(0, 'a', 'A'),
(1, 'b', 'B'),
)
def test_nonexistent_identifiers_raise(self):
with self.assertRaises(ValueError):
self.choices.subset('a', 'c')
def test_solo_nonexistent_identifiers_raise(self):
with self.assertRaises(ValueError):
self.choices.subset('c')
def test_empty_subset_passes(self):
subset = self.choices.subset()
self.assertEqual(subset, Choices())
def test_subset_returns_correct_subset(self):
subset = self.choices.subset('a')
self.assertEqual(subset, Choices((0, 'a', 'A')))