Fixed #20430 - Enable iterable of iterables for model choices

Allows for any iterable, not just lists or tuples, to be used as
the inner item for a list of choices in a model.
This commit is contained in:
Donald Stufft 2013-05-17 15:31:41 -04:00
parent a0c0cc924e
commit a19e9d80ff
7 changed files with 52 additions and 7 deletions

View File

@ -118,8 +118,8 @@ def get_validation_errors(outfile, app=None):
e.add(opts, '"%s": "choices" should be iterable (e.g., a tuple or list).' % f.name) e.add(opts, '"%s": "choices" should be iterable (e.g., a tuple or list).' % f.name)
else: else:
for c in f.choices: for c in f.choices:
if not isinstance(c, (list, tuple)) or len(c) != 2: if isinstance(c, six.string_types) or not is_iterable(c) or len(c) != 2:
e.add(opts, '"%s": "choices" should be a sequence of two-tuples.' % f.name) e.add(opts, '"%s": "choices" should be a sequence of two-item iterables (e.g. list of 2 item tuples).' % f.name)
if f.db_index not in (None, True, False): if f.db_index not in (None, True, False):
e.add(opts, '"%s": "db_index" should be either None, True or False.' % f.name) e.add(opts, '"%s": "db_index" should be either None, True or False.' % f.name)

View File

@ -80,9 +80,10 @@ If a field has ``blank=False``, the field will be required.
.. attribute:: Field.choices .. attribute:: Field.choices
An iterable (e.g., a list or tuple) of 2-tuples to use as choices for this An iterable (e.g., a list or tuple) consisting itself of iterables of exactly
field. If this is given, the default form widget will be a select box with two items (e.g. ``[(A, B), (A, B) ...]``) to use as choices for this field. If
these choices instead of the standard text field. this is given, the default form widget will be a select box with these choices
instead of the standard text field.
The first element in each tuple is the actual value to be stored, and the The first element in each tuple is the actual value to be stored, and the
second element is the human-readable name. For example:: second element is the human-readable name. For example::

View File

@ -238,6 +238,9 @@ Minor features
Meta option: ``localized_fields``. Fields included in this list will be localized Meta option: ``localized_fields``. Fields included in this list will be localized
(by setting ``localize`` on the form field). (by setting ``localize`` on the form field).
* The ``choices`` argument to model fields now accepts an iterable of iterables
instead of requiring an iterable of lists or tuples.
Backwards incompatible changes in 1.6 Backwards incompatible changes in 1.6
===================================== =====================================

View File

@ -375,8 +375,8 @@ invalid_models.fielderrors: "decimalfield3": DecimalFields require a "max_digits
invalid_models.fielderrors: "decimalfield4": DecimalFields require a "max_digits" attribute value that is greater than or equal to the value of the "decimal_places" attribute. invalid_models.fielderrors: "decimalfield4": DecimalFields require a "max_digits" attribute value that is greater than or equal to the value of the "decimal_places" attribute.
invalid_models.fielderrors: "filefield": FileFields require an "upload_to" attribute. invalid_models.fielderrors: "filefield": FileFields require an "upload_to" attribute.
invalid_models.fielderrors: "choices": "choices" should be iterable (e.g., a tuple or list). invalid_models.fielderrors: "choices": "choices" should be iterable (e.g., a tuple or list).
invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-tuples. invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-item iterables (e.g. list of 2 item tuples).
invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-tuples. invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-item iterables (e.g. list of 2 item tuples).
invalid_models.fielderrors: "index": "db_index" should be either None, True or False. invalid_models.fielderrors: "index": "db_index" should be either None, True or False.
invalid_models.fielderrors: "field_": Field names cannot end with underscores, because this would lead to ambiguous queryset filters. invalid_models.fielderrors: "field_": Field names cannot end with underscores, because this would lead to ambiguous queryset filters.
invalid_models.fielderrors: "nullbool": BooleanFields do not accept null values. Use a NullBooleanField instead. invalid_models.fielderrors: "nullbool": BooleanFields do not accept null values. Use a NullBooleanField instead.

View File

View File

@ -0,0 +1,27 @@
from django.db import models
class ThingItem(object):
def __init__(self, value, display):
self.value = value
self.display = display
def __iter__(self):
return (x for x in [self.value, self.display])
def __len__(self):
return 2
class Things(object):
def __iter__(self):
return (x for x in [ThingItem(1, 2), ThingItem(3, 4)])
class ThingWithIterableChoices(models.Model):
# Testing choices= Iterable of Iterables
# See: https://code.djangoproject.com/ticket/20430
thing = models.CharField(max_length=100, blank=True, choices=Things())

View File

@ -0,0 +1,14 @@
import io
from django.core import management
from django.test import TestCase
class ModelValidationTest(TestCase):
def test_models_validate(self):
# All our models should validate properly
# Validation Tests:
# * choices= Iterable of Iterables
# See: https://code.djangoproject.com/ticket/20430
management.call_command("validate", stdout=io.BytesIO())