I tried to implement this as a choices variable for a model:
>>> choices = (
... (1, 'Primary'), (2, 'Secondary'), (3, 'Tertiary')
... )
But it failed with this quasi-cryptic error:
characters.CharacterArcanumLink.priority: (fields.E005) 'choices' must
be an iterable containing (actual value, human readable name) tuples.
Lets see,
- choices is an iterable? Check.
- containing (actual value, human readable name) tuples? Check: The Actual value is an integer, and the 'human readable name' is a string.
Looks ok.
Digging into the code, it looks like new checks are done for Django 1.7:
def _check_choices(self):
if self.choices:
if (isinstance(self.choices, six.string_types) or
not is_iterable(self.choices)):
return [
checks.Error(
"'choices' must be an iterable (e.g., a list or tuple).",
hint=None,
obj=self,
id='fields.E004',
)
]
elif any(isinstance(choice, six.string_types) or
not is_iterable(choice) or len(choice) != 2
for choice in self.choices):
return [
checks.Error(
("'choices' must be an iterable containing "
"(actual value, human readable name) tuples."),
hint=None,
obj=self,
id='fields.E005',
)
]
else:
return []
else:
return []
Its the second check, after the elif that looks fishy. Lets try that isinstance
on my choices
tuple above:
>>> any(isinstance(choice, six.string_types) for choice in choices)
False
No good! How about with some modification?
>>> any(isinstance(choice[1], six.string_types) for choice in choices)
True
>>>
Excellent.
My question is, have I missed something? I'm sure this used to be possible. Why is it no longer possible? Am I implementing this wrongly?
I've also opened a ticket on code.djangoproject, if that helps?
The code that trigger it comes in two parts:
class Trait(models.Model):
PRIORITY_CHOICES = (
(None, '')
)
MIN = 0
MAX = 5
current_value = IntegerRangeField(min_value=MIN, max_value=MAX)
maximum_value = IntegerRangeField(min_value=MIN, max_value=MAX)
priority = models.PositiveSmallIntegerField(choices=PRIORITY_CHOICES, default=None)
class Meta:
abstract = True
And:
class CharacterSkillLink(CharacterLink, SkillLink, Trait):
PRIORITY_CHOICES = (
(1, 'Primary'), (2, 'Secondary'), (3, 'Tertiary')
)
speciality = models.CharField(max_length=200)
The issue is when you have a single choice - the outer brackets don't construct a tuple, just provide grouping. The creation of a tuple is provided by the comma, not the brackets.
You should leave a trailing comma to signify that you want a single-item tuple (or use a list):