/model_utils/__init__.py

https://github.com/asavoy/django-model-utils · Python · 135 lines · 113 code · 3 blank · 19 comment · 2 complexity · 634cf59d7aae96f2beeeaf8a51e4aff5 MD5 · raw file

  1. from django import VERSION
  2. if VERSION < (1, 2):
  3. import warnings
  4. warnings.warn(
  5. "Django 1.1 support in django-model-utils is pending deprecation.",
  6. PendingDeprecationWarning)
  7. class ChoiceEnum(object):
  8. """
  9. DEPRECATED: Use ``Choices`` (below) instead. This class has less
  10. flexibility for human-readable display, and greater potential for
  11. surprising data corruption if new choices are inserted in the
  12. middle of the list. Automatic assignment of numeric IDs is not
  13. such a great idea after all.
  14. A class to encapsulate handy functionality for lists of choices
  15. for a Django model field.
  16. Accepts verbose choice names as arguments, and automatically
  17. assigns numeric keys to them. When iterated over, behaves as the
  18. standard Django choices list of two-tuples.
  19. Attribute access allows conversion of verbose choice name to
  20. choice key, dictionary access the reverse.
  21. Example:
  22. >>> STATUS = ChoiceEnum('DRAFT', 'PUBLISHED')
  23. >>> STATUS.DRAFT
  24. 0
  25. >>> STATUS[1]
  26. 'PUBLISHED'
  27. >>> tuple(STATUS)
  28. ((0, 'DRAFT'), (1, 'PUBLISHED'))
  29. """
  30. def __init__(self, *choices):
  31. import warnings
  32. warnings.warn("ChoiceEnum is deprecated, use Choices instead.",
  33. DeprecationWarning)
  34. self._choices = tuple(enumerate(choices))
  35. self._choice_dict = dict(self._choices)
  36. self._reverse_dict = dict(((i[1], i[0]) for i in self._choices))
  37. def __iter__(self):
  38. return iter(self._choices)
  39. def __getattr__(self, attname):
  40. try:
  41. return self._reverse_dict[attname]
  42. except KeyError:
  43. raise AttributeError(attname)
  44. def __getitem__(self, key):
  45. return self._choice_dict[key]
  46. def __repr__(self):
  47. return '%s(%s)' % (self.__class__.__name__,
  48. ', '.join(("'%s'" % i[1] for i in self._choices)))
  49. class Choices(object):
  50. """
  51. A class to encapsulate handy functionality for lists of choices
  52. for a Django model field.
  53. Each argument to ``Choices`` is a choice, represented as either a
  54. string, a two-tuple, or a three-tuple.
  55. If a single string is provided, that string is used as the
  56. database representation of the choice as well as the
  57. human-readable presentation.
  58. If a two-tuple is provided, the first item is used as the database
  59. representation and the second the human-readable presentation.
  60. If a triple is provided, the first item is the database
  61. representation, the second a valid Python identifier that can be
  62. used as a readable label in code, and the third the human-readable
  63. presentation. This is most useful when the database representation
  64. must sacrifice readability for some reason: to achieve a specific
  65. ordering, to use an integer rather than a character field, etc.
  66. Regardless of what representation of each choice is originally
  67. given, when iterated over or indexed into, a ``Choices`` object
  68. behaves as the standard Django choices list of two-tuples.
  69. If the triple form is used, the Python identifier names can be
  70. accessed as attributes on the ``Choices`` object, returning the
  71. database representation. (If the single or two-tuple forms are
  72. used and the database representation happens to be a valid Python
  73. identifier, the database representation itself is available as an
  74. attribute on the ``Choices`` object, returning itself.)
  75. """
  76. def __init__(self, *choices):
  77. self._full = []
  78. self._choices = []
  79. self._choice_dict = {}
  80. for choice in self.equalize(choices):
  81. self._full.append(choice)
  82. self._choices.append((choice[0], choice[2]))
  83. self._choice_dict[choice[1]] = choice[0]
  84. def equalize(self, choices):
  85. for choice in choices:
  86. if isinstance(choice, (list, tuple)):
  87. if len(choice) == 3:
  88. yield choice
  89. elif len(choice) == 2:
  90. yield (choice[0], choice[0], choice[1])
  91. else:
  92. raise ValueError("Choices can't handle a list/tuple of length %s, only 2 or 3"
  93. % len(choice))
  94. else:
  95. yield (choice, choice, choice)
  96. def __iter__(self):
  97. return iter(self._choices)
  98. def __getattr__(self, attname):
  99. try:
  100. return self._choice_dict[attname]
  101. except KeyError:
  102. raise AttributeError(attname)
  103. def __getitem__(self, index):
  104. return self._choices[index]
  105. def __repr__(self):
  106. return '%s(%s)' % (self.__class__.__name__,
  107. ', '.join(("%s" % str(i) for i in self._full)))