Are numerically keyed choices no longer possible i

2019-07-20 01:02发布

问题:

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,

  1. choices is an iterable? Check.
  2. 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)

回答1:

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.

((None, '')) == (None, '')

You should leave a trailing comma to signify that you want a single-item tuple (or use a list):

PRIORITY_CHOICES = ((None, ''), )