title: Are numerically keyed choices no longer possible in Django 1.7?
body: <p>I tried to implement this as a choices variable for a model:</p>
  
  <pre><code>&gt;&gt;&gt; choices = (
  ...         (1, 'Primary'), (2, 'Secondary'), (3, 'Tertiary')
  ...         )
  </code></pre>
  
  <p>But it failed with this quasi-cryptic error:</p>
  
  <pre><code>characters.CharacterArcanumLink.priority: (fields.E005) 'choices' must
  be an iterable containing (actual value, human readable name) tuples.
  </code></pre>
  
  <p>Lets see, </p>
  
  <ol>
  <li>choices is an iterable? Check.</li>
  <li>containing (actual value, human readable name) tuples? Check: The Actual value is an integer, and the 'human readable name' is a string.</li>
  </ol>
  
  <p>Looks ok.</p>
  
  <p>Digging into the <a href="https://github.com/django/django/blob/master/django/db/models/fields/__init__.py#L242" rel="nofollow">code</a>, it looks like new checks are done for Django 1.7:</p>
  
  <pre><code>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 []
  </code></pre>
  
  <p>Its the second check, after the elif that looks fishy. Lets try that <code>isinstance</code> on my <code>choices</code> tuple above:</p>
  
  <pre><code>&gt;&gt;&gt; any(isinstance(choice, six.string_types) for choice in choices)
  False
  </code></pre>
  
  <p>No good! How about with some modification?</p>
  
  <pre><code>&gt;&gt;&gt; any(isinstance(choice[1], six.string_types) for choice in choices)
  True
  &gt;&gt;&gt;
  </code></pre>
  
  <p>Excellent.</p>
  
  <p>My question is, have I missed something? I'm <a href="http://www.b-list.org/weblog/2007/nov/02/handle-choices-right-way/" rel="nofollow">sure</a> this used to be possible. Why is it no longer possible? Am I implementing this wrongly?</p>
  
  <p>I've also opened a <a href="https://code.djangoproject.com/ticket/24271" rel="nofollow" title="#24271">ticket</a> on code.djangoproject, if that helps?</p>
  
  <hr>
  
  <p>The code that trigger it comes in two parts:</p>
  
  <pre><code>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
  </code></pre>
  
  <p>And:</p>
  
  <pre><code>class CharacterSkillLink(CharacterLink, SkillLink, Trait):
      PRIORITY_CHOICES = (
          (1, 'Primary'), (2, 'Secondary'), (3, 'Tertiary')
          )
      speciality = models.CharField(max_length=200)
  </code></pre>