Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge branch 'allow-any-iterable-for-choices'

  • Loading branch information...
commit 398841d6d3fce45b21bdfcd8385889098079f84a 2 parents 78de9b1 + a19e9d8
Donald Stufft authored May 18, 2013
4  django/core/management/validation.py
@@ -118,8 +118,8 @@ def get_validation_errors(outfile, app=None):
118 118
                     e.add(opts, '"%s": "choices" should be iterable (e.g., a tuple or list).' % f.name)
119 119
                 else:
120 120
                     for c in f.choices:
121  
-                        if not isinstance(c, (list, tuple)) or len(c) != 2:
122  
-                            e.add(opts, '"%s": "choices" should be a sequence of two-tuples.' % f.name)
  121
+                        if isinstance(c, six.string_types) or not is_iterable(c) or len(c) != 2:
  122
+                            e.add(opts, '"%s": "choices" should be a sequence of two-item iterables (e.g. list of 2 item tuples).' % f.name)
123 123
             if f.db_index not in (None, True, False):
124 124
                 e.add(opts, '"%s": "db_index" should be either None, True or False.' % f.name)
125 125
 
7  docs/ref/models/fields.txt
@@ -80,9 +80,10 @@ If a field has ``blank=False``, the field will be required.
80 80
 
81 81
 .. attribute:: Field.choices
82 82
 
83  
-An iterable (e.g., a list or tuple) of 2-tuples to use as choices for this
84  
-field. If this is given, the default form widget will be a select box with
85  
-these choices instead of the standard text field.
  83
+An iterable (e.g., a list or tuple) consisting itself of iterables of exactly
  84
+two items (e.g. ``[(A, B), (A, B) ...]``) to use as choices for this field. If
  85
+this is given, the default form widget will be a select box with these choices
  86
+instead of the standard text field.
86 87
 
87 88
 The first element in each tuple is the actual value to be stored, and the
88 89
 second element is the human-readable name. For example::
3  docs/releases/1.6.txt
@@ -238,6 +238,9 @@ Minor features
238 238
   Meta option: ``localized_fields``. Fields included in this list will be localized
239 239
   (by setting ``localize`` on the form field).
240 240
 
  241
+* The ``choices`` argument to model fields now accepts an iterable of iterables
  242
+  instead of requiring an iterable of lists or tuples.
  243
+
241 244
 Backwards incompatible changes in 1.6
242 245
 =====================================
243 246
 
4  tests/invalid_models/invalid_models/models.py
@@ -375,8 +375,8 @@ class Meta:
375 375
 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.
376 376
 invalid_models.fielderrors: "filefield": FileFields require an "upload_to" attribute.
377 377
 invalid_models.fielderrors: "choices": "choices" should be iterable (e.g., a tuple or list).
378  
-invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-tuples.
379  
-invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-tuples.
  378
+invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-item iterables (e.g. list of 2 item tuples).
  379
+invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-item iterables (e.g. list of 2 item tuples).
380 380
 invalid_models.fielderrors: "index": "db_index" should be either None, True or False.
381 381
 invalid_models.fielderrors: "field_": Field names cannot end with underscores, because this would lead to ambiguous queryset filters.
382 382
 invalid_models.fielderrors: "nullbool": BooleanFields do not accept null values. Use a NullBooleanField instead.
0  tests/model_validation/__init__.py
No changes.
27  tests/model_validation/models.py
... ...
@@ -0,0 +1,27 @@
  1
+from django.db import models
  2
+
  3
+
  4
+class ThingItem(object):
  5
+
  6
+    def __init__(self, value, display):
  7
+        self.value = value
  8
+        self.display = display
  9
+
  10
+    def __iter__(self):
  11
+        return (x for x in [self.value, self.display])
  12
+
  13
+    def __len__(self):
  14
+        return 2
  15
+
  16
+
  17
+class Things(object):
  18
+
  19
+    def __iter__(self):
  20
+        return (x for x in [ThingItem(1, 2), ThingItem(3, 4)])
  21
+
  22
+
  23
+class ThingWithIterableChoices(models.Model):
  24
+
  25
+    # Testing choices= Iterable of Iterables
  26
+    #   See: https://code.djangoproject.com/ticket/20430
  27
+    thing = models.CharField(max_length=100, blank=True, choices=Things())
14  tests/model_validation/tests.py
... ...
@@ -0,0 +1,14 @@
  1
+import io
  2
+
  3
+from django.core import management
  4
+from django.test import TestCase
  5
+
  6
+
  7
+class ModelValidationTest(TestCase):
  8
+
  9
+    def test_models_validate(self):
  10
+        # All our models should validate properly
  11
+        # Validation Tests:
  12
+        #   * choices= Iterable of Iterables
  13
+        #       See: https://code.djangoproject.com/ticket/20430
  14
+        management.call_command("validate", stdout=io.BytesIO())

0 notes on commit 398841d

Please sign in to comment.
Something went wrong with that request. Please try again.