try:
from collections import namedtuple
except ImportError:
# Python 2.4, 2.5 backport:
# http://code.activestate.com/recipes/500261/
from somewhere.my_namedtuple_backport import namedtuple
def get_namedtuple_choices(name, choices_tuple):
"""Factory function for quickly making a namedtuple suitable for use in a
Django model as a choices attribute on a field. It will preserve order.
Usage::
class MyModel(models.Model):
COLORS = get_namedtuple_choices('COLORS', (
(0, 'BLACK', 'Black'),
(1, 'WHITE', 'White'),
))
colors = models.PositiveIntegerField(choices=COLORS)
>>> MyModel.COLORS.BLACK
0
>>> MyModel.COLORS.get_choices()
[(0, 'Black'), (1, 'White')]
class OtherModel(models.Model):
GRADES = get_namedtuple_choices('GRADES', (
('FR', 'FR', 'Freshman'),
('SR', 'SR', 'Senior'),
))
grade = models.CharField(max_length=2, choices=GRADES)
>>> OtherModel.GRADES.FR
'FR'
>>> OtherModel.GRADES.get_choices()
[('FR', 'Freshman'), ('SR', 'Senior')]
"""
class Choices(namedtuple(name, [name for val,name,desc in choices_tuple])):
__slots__ = ()
_choices = tuple([desc for val,name,desc in choices_tuple])
def get_choices(self):
return zip(tuple(self), self._choices)
return Choices._make([val for val,name,desc in choices_tuple])
Comments
Great idea!
1) You don't need to use
namedtuplethere, you can just usesetattron class to add methods after class creation, and very simple__str__method (exactly what nametuple backport snippet does).2) Why do I need to write name of choices group twice?
#
I'm subclassing
namedtuplebecause it avoids the extra boilerplate that rolling your ownsetattrclass requires and allows a clean and full-featured choices solution in seven lines of code. :)Read the examples in the docstring. It is setting
(value, code-friendly name, human-readable name).#
I don't get how that is better than the common way of defining choices.
#
Read James Bennett's blog post linked in the snippet description. Also look at the dozen or so other implementations on this site.
#