extended_choices.choices module

Provides a Choices class to help using “choices” in Django fields.

The aim is to replace:

STATE_ONLINE  = 1
STATE_DRAFT   = 2
STATE_OFFLINE = 3

STATE_CHOICES = (
    (STATE_ONLINE,  'Online'),
    (STATE_DRAFT,   'Draft'),
    (STATE_OFFLINE, 'Offline'),
)

STATE_DICT = dict(STATE_CHOICES)

class Content(models.Model):
    title      = models.CharField(max_length=255)
    content    = models.TextField()
    state      = models.PositiveSmallIntegerField(choices=STATE_CHOICES, default=STATE_DRAFT)

    def __unicode__(self):
        return 'Content "%s" (state=%s)' % (self.title, STATE_DICT[self.state])

print(Content.objects.filter(state=STATE_ONLINE))

By this:

from extended_choices import Choices

STATES = Choices(
    ('ONLINE',  1, 'Online'),
    ('DRAFT',   2, 'Draft'),
    ('OFFLINE', 3, 'Offline'),
)

class Content(models.Model):
    title      = models.CharField(max_length=255)
    content    = models.TextField()
    state      = models.PositiveSmallIntegerField(choices=STATES, default=STATES.DRAFT)

    def __unicode__(self):
        return 'Content "%s" (state=%s)' % (self.title, STATES.for_value(self.state).display)

print(Content.objects.filter(state=STATES.ONLINE))

Notes

The documentation format in this file is numpydoc.

class extended_choices.choices.Choices(*choices, **kwargs)[source]

Bases: list

Helper class for choices fields in Django

A choice entry has three representation: constant name, value and display name). So Choices takes list of such tuples.

It’s easy to get the constant, value or display name given one of these value. See in example.

Parameters:
  • *choices (list of tuples) –

    It’s the list of tuples to add to the Choices instance, each tuple having three entries: the constant name, the value, the display name.

    A dict could be added as a 4th entry in the tuple to allow setting arbitrary arguments to the final ChoiceEntry created for this choice tuple.

  • name (string, optional) – If set, a subset will be created containing all the constants. It could be used if you construct your Choices instance with many calls to add_choices.
  • dict_class (type, optional) – dict by default, it’s the dict class to use to create dictionaries (constants, values and displays. Could be set for example to OrderedDict (you can use OrderedChoices that is a simple subclass using OrderedDict.

Example

Start by declaring your Choices:

>>> ALIGNMENTS = Choices(
...     ('BAD', 10, 'bad'),
...     ('NEUTRAL', 20, 'neutral'),
...     ('CHAOTIC_GOOD', 30, 'chaotic good'),
...     ('GOOD', 40, 'good'),
...     dict_class=OrderedDict
... )

Then you can use it in a django field, Notice its usage in choices and default:

>>> from django.conf import settings
>>> try:
...     settings.configure(DATABASE_ENGINE='sqlite3')
... except: pass
>>> from django.db.models import IntegerField
>>> field = IntegerField(choices=ALIGNMENTS,  # use ``ALIGNMENTS`` or ``ALIGNMENTS.choices``.
...                      default=ALIGNMENTS.NEUTRAL)

The Choices returns a list as expected by django:

>>> ALIGNMENTS == ((10, 'bad'), (20, 'neutral'), (30, 'chaotic good'), (40, 'good'))
True

But represents it with the constants:

>>> repr(ALIGNMENTS)
"[('BAD', 10, 'bad'), ('NEUTRAL', 20, 'neutral'), ('CHAOTIC_GOOD', 30, 'chaotic good'), ('GOOD', 40, 'good')]"

Use choices which is a simple list to represent it as such:

>>> ALIGNMENTS.choices
((10, 'bad'), (20, 'neutral'), (30, 'chaotic good'), (40, 'good'))

And you can access value by their constant, or as you want:

>>> ALIGNMENTS.BAD
10
>>> ALIGNMENTS.BAD.display
'bad'
>>> 40 in ALIGNMENTS
True
>>> ALIGNMENTS.has_constant('BAD')
True
>>> ALIGNMENTS.has_value(20)
True
>>> ALIGNMENTS.has_display('good')
True
>>> ALIGNMENTS.for_value(10)
('BAD', 10, 'bad')
>>> ALIGNMENTS.for_value(10).constant
'BAD'
>>> ALIGNMENTS.for_display('good').value
40
>>> ALIGNMENTS.for_constant('NEUTRAL').display
'neutral'
>>> ALIGNMENTS.constants
OrderedDict([('BAD', ('BAD', 10, 'bad')), ('NEUTRAL', ('NEUTRAL', 20, 'neutral')), ('CHAOTIC_GOOD', ('CHAOTIC_GOOD', 30, 'chaotic good')), ('GOOD', ('GOOD', 40, 'good'))])
>>> ALIGNMENTS.values
OrderedDict([(10, ('BAD', 10, 'bad')), (20, ('NEUTRAL', 20, 'neutral')), (30, ('CHAOTIC_GOOD', 30, 'chaotic good')), (40, ('GOOD', 40, 'good'))])
>>> ALIGNMENTS.displays
OrderedDict([('bad', ('BAD', 10, 'bad')), ('neutral', ('NEUTRAL', 20, 'neutral')), ('chaotic good', ('CHAOTIC_GOOD', 30, 'chaotic good')), ('good', ('GOOD', 40, 'good'))])

You can create subsets of choices:

>>> ALIGNMENTS.add_subset('WESTERN',('BAD', 'GOOD'))
>>> ALIGNMENTS.WESTERN.choices
((10, 'bad'), (40, 'good'))
>>> ALIGNMENTS.BAD in ALIGNMENTS.WESTERN
True
>>> ALIGNMENTS.NEUTRAL in ALIGNMENTS.WESTERN
False

To use it in another field (only the values in the subset will be available), or for checks:

>>> def is_western(value):
...     return value in ALIGNMENTS.WESTERN
>>> is_western(40)
True
ChoiceEntryClass

alias of ChoiceEntry

add_choices(*choices, **kwargs)[source]

Add some choices to the current Choices instance.

The given choices will be added to the existing choices. If a name attribute is passed, a new subset will be created with all the given choices.

Note that it’s not possible to add new choices to a subset.

Parameters:
  • *choices (list of tuples) –

    It’s the list of tuples to add to the Choices instance, each tuple having three entries: the constant name, the value, the display name.

    A dict could be added as a 4th entry in the tuple to allow setting arbitrary arguments to the final ChoiceEntry created for this choice tuple.

    If the first entry of *choices is a string, then it will be used as a name for a new subset that will contain all the given choices.

  • **kwargs (dict) –
    name : string
    Instead of using the first entry of the *choices to pass a name of a subset to create, you can pass it via the name named argument.

Example

>>> MY_CHOICES = Choices()
>>> MY_CHOICES.add_choices(('ZERO', 0, 'zero'))
>>> MY_CHOICES
[('ZERO', 0, 'zero')]
>>> MY_CHOICES.add_choices('SMALL', ('ONE', 1, 'one'), ('TWO', 2, 'two'))
>>> MY_CHOICES
[('ZERO', 0, 'zero'), ('ONE', 1, 'one'), ('TWO', 2, 'two')]
>>> MY_CHOICES.SMALL
[('ONE', 1, 'one'), ('TWO', 2, 'two')]
>>> MY_CHOICES.add_choices(('THREE', 3, 'three'), ('FOUR', 4, 'four'), name='BIG')
>>> MY_CHOICES
[('ZERO', 0, 'zero'), ('ONE', 1, 'one'), ('TWO', 2, 'two'), ('THREE', 3, 'three'), ('FOUR', 4, 'four')]
>>> MY_CHOICES.BIG
[('THREE', 3, 'three'), ('FOUR', 4, 'four')]
Raises:
  • RuntimeError – When the Choices instance is marked as not mutable, which is the case for subsets.
  • ValueError – * if the subset name is defined as first argument and as named argument. * if some constants have the same name or the same value. * if at least one constant or value already exists in the instance.
add_subset(name, constants)[source]

Add a subset of entries under a defined name.

This allow to defined a “sub choice” if a django field need to not have the whole choice available.

The sub-choice is a new Choices instance, with only the wanted the constant from the main Choices (each “choice entry” in the subset is shared from the main Choices) The sub-choice is accessible from the main Choices by an attribute having the given name.

Parameters:
  • name (string) – Name of the attribute that will old the new Choices instance.
  • constants (list or tuple) – List of the constants name of this Choices object to make available in the subset.
Returns:

The newly created subset, which is a Choices object

Return type:

Choices

Example

>>> STATES = Choices(
...     ('ONLINE',  1, 'Online'),
...     ('DRAFT',   2, 'Draft'),
...     ('OFFLINE', 3, 'Offline'),
... )
>>> STATES
[('ONLINE', 1, 'Online'), ('DRAFT', 2, 'Draft'), ('OFFLINE', 3, 'Offline')]
>>> STATES.add_subset('NOT_ONLINE', ('DRAFT', 'OFFLINE',))
>>> STATES.NOT_ONLINE
[('DRAFT', 2, 'Draft'), ('OFFLINE', 3, 'Offline')]
>>> STATES.NOT_ONLINE.DRAFT
2
>>> STATES.NOT_ONLINE.for_constant('DRAFT') is STATES.for_constant('DRAFT')
True
>>> STATES.NOT_ONLINE.ONLINE
Traceback (most recent call last):
...
AttributeError: 'Choices' object has no attribute 'ONLINE'
Raises:ValueError – * If name is already an attribute of the Choices instance. * If a constant is not defined as a constant in the Choices instance.
choices

Property that returns a tuple formatted as expected by Django.

Example

>>> MY_CHOICES = Choices(('FOO', 1, 'foo'), ('BAR', 2, 'bar'))
>>> MY_CHOICES.choices
((1, 'foo'), (2, 'bar'))
extract_subset(*constants)[source]

Create a subset of entries

This subset is a new Choices instance, with only the wanted constants from the main Choices (each “choice entry” in the subset is shared from the main Choices)

Parameters:*constants (list) – The constants names of this Choices object to make available in the subset.
Returns:The newly created subset, which is a Choices object
Return type:Choices

Example

>>> STATES = Choices(
...     ('ONLINE',  1, 'Online'),
...     ('DRAFT',   2, 'Draft'),
...     ('OFFLINE', 3, 'Offline'),
... )
>>> STATES
[('ONLINE', 1, 'Online'), ('DRAFT', 2, 'Draft'), ('OFFLINE', 3, 'Offline')]
>>> subset = STATES.extract_subset('DRAFT', 'OFFLINE')
>>> subset
[('DRAFT', 2, 'Draft'), ('OFFLINE', 3, 'Offline')]
>>> subset.DRAFT
2
>>> subset.for_constant('DRAFT') is STATES.for_constant('DRAFT')
True
>>> subset.ONLINE
Traceback (most recent call last):
...
AttributeError: 'Choices' object has no attribute 'ONLINE'
Raises:ValueError – If a constant is not defined as a constant in the Choices instance.
for_constant(constant)[source]

Returns the ChoiceEntry for the given constant.

Parameters:constant (string) – Name of the constant for which we want the choice entry.
Returns:The instance of ChoiceEntry for the given constant.
Return type:ChoiceEntry
Raises:KeyError – If the constant is not an existing one.

Example

>>> MY_CHOICES = Choices(('FOO', 1, 'foo'), ('BAR', 2, 'bar'))
>>> MY_CHOICES.for_constant('FOO')
('FOO', 1, 'foo')
>>> MY_CHOICES.for_constant('FOO').value
1
>>> MY_CHOICES.for_constant('QUX')
Traceback (most recent call last):
...
KeyError: 'QUX'
for_display(display)[source]

Returns the ChoiceEntry for the given display name.

Parameters:display (string) – Display name for which we want the choice entry.
Returns:The instance of ChoiceEntry for the given display name.
Return type:ChoiceEntry
Raises:KeyError – If the display name is not an existing one.

Example

>>> MY_CHOICES = Choices(('FOO', 1, 'foo'), ('BAR', 2, 'bar'))
>>> MY_CHOICES.for_display('foo')
('FOO', 1, 'foo')
>>> MY_CHOICES.for_display('foo').constant
'FOO'
>>> MY_CHOICES.for_display('qux')
Traceback (most recent call last):
...
KeyError: 'qux'
for_value(value)[source]

Returns the ChoiceEntry for the given value.

Parameters:value – Value for which we want the choice entry.
Returns:The instance of ChoiceEntry for the given value.
Return type:ChoiceEntry
Raises:KeyError – If the value is not an existing one.

Example

>>> MY_CHOICES = Choices(('FOO', 1, 'foo'), ('BAR', 2, 'bar'))
>>> MY_CHOICES.for_value(1)
('FOO', 1, 'foo')
>>> MY_CHOICES.for_value(1).display
'foo'
>>> MY_CHOICES.for_value(3)
Traceback (most recent call last):
...
KeyError: 3
has_constant(constant)[source]

Check if the current Choices object has the given constant.

Parameters:constant (string) – Name of the constant we want to check..
Returns:True if the constant is present, False otherwise.
Return type:boolean

Example

>>> MY_CHOICES = Choices(('FOO', 1, 'foo'), ('BAR', 2, 'bar'))
>>> MY_CHOICES.has_constant('FOO')
True
>>> MY_CHOICES.has_constant('QUX')
False
has_display(display)[source]

Check if the current Choices object has the given display name.

Parameters:display (string) – Display name we want to check..
Returns:True if the display name is present, False otherwise.
Return type:boolean

Example

>>> MY_CHOICES = Choices(('FOO', 1, 'foo'), ('BAR', 2, 'bar'))
>>> MY_CHOICES.has_display('foo')
True
>>> MY_CHOICES.has_display('qux')
False
has_value(value)[source]

Check if the current Choices object has the given value.

Parameters:value – Value we want to check.
Returns:True if the value is present, False otherwise.
Return type:boolean

Example

>>> MY_CHOICES = Choices(('FOO', 1, 'foo'), ('BAR', 2, 'bar'))
>>> MY_CHOICES.has_value(1)
True
>>> MY_CHOICES.has_value(3)
False