Better choices library for Django web framework.
This library was written for Python 3.7+ and will not work in any earlier versions.
pip install django-better-choices
To start defining better choices, you need first to import the Choices
class.
from django_better_choices import Choices
The choices can be defined with overriding Choices
class.
class PageStatus(Choices):
CREATED = 'Created'
PENDING = Choices.Value('Pending', help_text='This set status to pending')
ON_HOLD = Choices.Value('On Hold', value='custom_on_hold')
VALID = Choices.Subset('CREATED', 'ON_HOLD')
INVISIBLE = Choices.Subset('PENDING', 'ON_HOLD')
class InternalStatus(Choices):
REVIEW = 'On Review'
@classmethod
def get_help_text(cls):
return tuple(
value.help_text
for value in cls.values()
if hasattr(value, 'help_text')
)
Choices class key can be any public identifier (i.e. not starting with underscore
_
). Overridden choices classes cannot be initialised to obtain a new instance, calling the instance will return a tuple of choice entries.
Alternatively, the choices can be defined dynamically by creating a new Choices
instance.
PageStatus = Choices('PageStatus', SUCCESS='Success', FAIL='Error')
The first
name
parameter ofChoices
constructor is optional and required only for better representation of the returned instance.
You can access choices values using dot notation and with getattr()
.
value_created = PageStatus.CREATED
value_review = PageStatus.InternalStatus.REVIEW
value_on_hold = getattr(PageStatus, 'ON_HOLD')
Choices.Value
is a subclass of str
and equals to its value. In addition to display
parameter, other optional parameters can be specified in Choices.Value
constructor (see class definition example).
print( PageStatus.CREATED ) # 'created'
print( PageStatus.ON_HOLD ) # 'custom_on_hold'
print( PageStatus.PENDING.display ) # 'Pending'
print( PageStatus.PENDING.help_text ) # 'This set status to pending'
PageStatus.ON_HOLD == 'custom_on_hold' # True
PageStatus.CREATED == PageStatus.CREATED # True
Choices.Value
is an immutable string class, which instance cannot be modified after initialisation. Native non-magicstr
methods can be overridden inChoices.Value
custom parameters.Choices.Value
behaves like a normal string, e.g.{'val1': 'something'}[CHOICES.VAL1] == 'something'
.
Search in choices is performed by value.
'created' in PageStatus # True
'custom_on_hold' in PageStatus # True
'on_hold' in PageStatus # False
value = PageStatus['custom_on_hold'] # Choices.Value
value = PageStatus.get('on_hold', 123.45) # 123.45
key = PageStatus.get_key('created') # 'CREATED'
Subsets are used to group several values together (see class definition example) and search by a specific value.
'custom_on_hold' in PageStatus.VALID # True
PageStatus.CREATED in PageStatus.VALID # True
Choices.Subset
is a subclass oftuple
, which is translated to inner choices class after definition. All internal or custom choices class methods or properties will be available in a subset class (see "Custom methods" section).
Subsets of choices can be dynamically extracted with extract()
method.
PageStatus.extract('CREATED', 'ON_HOLD') # ~= PageStatus.VALID
PageStatus.VALID.extract('ON_HOLD') # Choices('PageStatus.VALID.Subset', ON_HOLD)
Choices class implements __iter__
magic method, hence choices are iterable that yield choice entries (i.e. (value, display)
). Methods items()
, keys()
and values()
can be used to return tuples of keys and values combinations.
for value, display in PageStatus: # can also be used as callable, i.e. PageStatus()
print( value, display )
for key, value in PageStatus.items():
print( key, value, value.display )
for key in PageStatus.keys():
print( key )
for value in PageStatus.values():
print( value, value.display, value.__choice_entry__ )
Additional displays()
method is provided for choices and subsets to extract values display strings.
for display in PageStatus.displays():
print( display )
for display in PageStatus.SUBSET.displays():
print( display )
Iteration methods
items()
,keys()
,values()
,displays()
, as well as class constructor can accept keyword arguments to filter collections based on custom parameters, e.g.PageStatus.values(help_text='Some', special=123)
.
Choices class and subsets support standard set operations: union (|
), intersection (&
), difference (-
), and symmetric difference (^
).
PageStatus.VALID | PageStatus.INVISIBLE # Choices(CREATED, ON_HOLD, PENDING)
PageStatus.VALID & PageStatus.INVISIBLE # Choices(ON_HOLD)
PageStatus.VALID - PageStatus.INVISIBLE # Choices(CREATED)
PageStatus.VALID ^ PageStatus.INVISIBLE # Choices(CREATED, PENDING)
All custom choices class methods or properties (non-values) will be available in all subsets.
PageStatus.get_help_text()
PageStatus.VALID.get_help_text()
PageStatus.extract('PENDING', 'ON_HOLD').get_help_text()
PageStatus.VALID.extract('ON_HOLD').get_help_text()
Better choices are not different from the original Django choices in terms of usage in models.
class Page(models.Model):
status = models.CharField(choices=PageStatus, default=PageStatus.CREATED)
Better choices are fully supported by Django migrations and debug toolbar.
Better choices are compatible with standard Django models manipulation.
page = Page.objects.get(pk=1)
page.status = PageStatus.PENDING
page.save()
Run python tests.py
for testing.
Library is available under the MIT license. The included LICENSE file describes this in detail.