django-extras / django_extras / db / models /

from collections import OrderedDict
from django.db.models.fields import NOT_PROVIDED

class ChoiceEnum(object):
    Defines a choice set for use with Django Models.

    ChoiceEnum can be used in one of two ways, due to a side effect of how dictionaries are sorted in Python
    (dictionaries and by extension **kwargs are implemented as a hash map) the order is not preserved.

    The nicest way to use ChoiceEnum (without guaranteed ordering):

        MY_CHOICES = ChoiceEnum(
            OPTION_ONE = ('value', 'Verbose value'),
            OPTION_TWO = ('value2', 'Verbose value 2', True), # Default, the value can be anything ;)

    The less nice way to use ChoiceEnum (with guaranteed ordering):

        MY_CHOICES = ChoiceEnum(
            ('OPTION_ONE', ('value', 'Verbose value')),
            ('OPTION_TWO', ('value2', 'Verbose value 2', True)), # Default, the value can be anything ;)

    They can then be used for field choices:

        foo = models.CharField(max_length=MY_CHOICES.max_length, choices=MY_CHOICES, default=MY_CHOICES.default)

    or with the kwargs helper:

        foo = models.CharField(**MY_CHOICES.kwargs)

    And values referenced using:


    Display values can be accessed via:


    Convert a value to a display string:

        MY_CHOICES % 'value'

    __slots__ = ('__choices', '__default', '__max_length', '__value_map')

    def __init__(self, *args, **entries):
        if args:
            entries = OrderedDict(args)
        if not entries:
            raise ValueError('No entries have been provided.')

        self.__default = NOT_PROVIDED
        self.__max_length = 0
        self.__value_map = dict()
        self.__choices = OrderedDict()

        map(self.__parse_entry, entries.iteritems())

    def __parse_entry(self, entry):
        key, value = entry

        if not isinstance(value, (tuple, list)):
            raise TypeError('Choice options should be a tuple or list.')
        value_len = len(value)
        if not value_len in (2, 3):
            raise ValueError('Expected choice entry in the form (Value, Verbose Value, [default]).')

        if value_len == 3:
            self.__default = value[0]
        if isinstance(value[0], basestring):
            self.__max_length = max(self.__max_length, len(value[0]))

        self.__value_map[value[0]] = value[1]
        self.__choices[key] = value[0], value[1]

    def __iter__(self):
        Return choice list for use in model definition.
        return iter(self.__choices.values())

    def __contains__(self, value):
        Check if a choice value is in this enum.

        @param value Choice value.
        return value in self.__value_map

    def __getattr__(self, item):
        Get the value of an Enum.
        if item.endswith('__display'):
            return self.__choices[item[:-9]][1]
            return self.__choices[item][0]

    def __mod__(self, other):
        Resolve a value to it's display version.
        return self.__value_map[other]

    def max_length(self):
        Length of maximum value
        return self.__max_length

    def default(self):
        The default value.
        return self.__default

    def kwargs(self):
        Helper to simplify assignment of choices to a model field.
        kwargs = {
            'choices': self,
            'default': self.__default,
        if self.__max_length:
            kwargs['max_length'] = self.__max_length
        return kwargs

    def resolve_value(self, value):
        Resolve a value to it's display version.
        return self.__value_map[value]