Ticket #13181: full_version.diff

File full_version.diff, 5.3 KB (added by Klaas van Schelven, 10 years ago)
  • django/forms/fields.py

    diff --git a/django/forms/fields.py b/django/forms/fields.py
    index 621d380..d7b6ebb 100644
    a b class NullBooleanField(BooleanField): 
    743743        return initial != data
    744744
    745745
     746class CallableChoiceIterator(object):
     747    def __init__(self, callme, field):
     748        self.callme = callme
     749        self.field = field
     750
     751    def __iter__(self):
     752        for e in self.callme(self.field):
     753            yield e
     754
    746755class ChoiceField(Field):
    747756    widget = Select
    748757    default_error_messages = {
    class ChoiceField(Field): 
    757766
    758767    def __deepcopy__(self, memo):
    759768        result = super(ChoiceField, self).__deepcopy__(memo)
    760         result._choices = copy.deepcopy(self._choices, memo)
     769        result._set_choices(copy.deepcopy(self._choices, memo))
    761770        return result
    762771
    763772    def _get_choices(self):
    class ChoiceField(Field): 
    765774
    766775    def _set_choices(self, value):
    767776        # Setting choices also sets the choices on the widget.
    768         # choices can be any iterable, but we call list() on it because
    769         # it will be consumed more than once.
    770         self._choices = self.widget.choices = list(value)
     777        if callable(value):
     778            value = CallableChoiceIterator(value, self)
     779        else:
     780            # choices can be any iterable, but we call list() on it because
     781            # it will be consumed more than once.
     782            value = list(value)
     783
     784        self._choices = self.widget.choices = value
    771785
    772786    choices = property(_get_choices, _set_choices)
    773787
  • django/forms/models.py

    diff --git a/django/forms/models.py b/django/forms/models.py
    index d545a07..006d8b1 100644
    a b from __future__ import absolute_import, unicode_literals 
    77
    88from django.core.exceptions import ValidationError, NON_FIELD_ERRORS, FieldError
    99from django.core.validators import EMPTY_VALUES
    10 from django.forms.fields import Field, ChoiceField
     10from django.forms.fields import Field, ChoiceField, CallableChoiceIterator
    1111from django.forms.forms import BaseForm, get_declared_fields
    1212from django.forms.formsets import BaseFormSet, formset_factory
    1313from django.forms.util import ErrorList
    class InlineForeignKeyField(Field): 
    899899
    900900class ModelChoiceIterator(object):
    901901    def __init__(self, field):
    902         self.field = field
    903         self.queryset = field.queryset
     902        self._delegate = CallableChoiceIterator(queryset_callable, field)
    904903
    905904    def __iter__(self):
    906         if self.field.empty_label is not None:
    907             yield ("", self.field.empty_label)
    908         if self.field.cache_choices:
    909             if self.field.choice_cache is None:
    910                 self.field.choice_cache = [
    911                     self.choice(obj) for obj in self.queryset.all()
    912                 ]
    913             for choice in self.field.choice_cache:
    914                 yield choice
    915         else:
    916             for obj in self.queryset.all():
    917                 yield self.choice(obj)
     905        return self._delegate.__iter__()
    918906
    919907    def __len__(self):
    920         return len(self.queryset)
    921 
    922     def choice(self, obj):
    923         return (self.field.prepare_value(obj), self.field.label_from_instance(obj))
     908        return len(self.field.queryset)
     909
     910
     911def queryset_callable(field):
     912    def choice(obj):
     913        return (field.prepare_value(obj), field.label_from_instance(obj))
     914
     915    if field.empty_label is not None:
     916        yield ("", field.empty_label)
     917    if field.cache_choices:
     918        if field.choice_cache is None:
     919            field.choice_cache = [
     920                choice(obj) for obj in field.queryset.all()
     921            ]
     922        for choice in field.choice_cache:
     923            yield choice
     924    else:
     925        for obj in field.queryset.all():
     926            yield choice(obj)
    924927
    925928class ModelChoiceField(ChoiceField):
    926929    """A ChoiceField whose choices are a model QuerySet."""
  • tests/regressiontests/forms/tests/fields.py

    diff --git a/tests/regressiontests/forms/tests/fields.py b/tests/regressiontests/forms/tests/fields.py
    index 3fe2cd2..bdce0ba 100644
    a b def fix_os_paths(x): 
    4949    else:
    5050        return x
    5151
    52 
    5352class FieldsTests(SimpleTestCase):
    5453
    5554    def assertWidgetRendersTo(self, field, to):
    class FieldsTests(SimpleTestCase): 
    893892        f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=False, empty_value=None)
    894893        self.assertEqual(None, f.clean(''))
    895894
     895    def test_choicefield_callable(self):
     896        choices = lambda field: [('J', 'John'), ('P', 'Paul')]
     897        f = ChoiceField(choices=choices)
     898        self.assertEqual(u'J', f.clean('J'))
     899     
     900    def test_choicefield_callable_may_evaluate_to_different_values(self):
     901        choices = []
     902        def choices_as_callable(field):
     903            return choices
     904
     905        class ChoiceFieldForm(Form):
     906            choicefield = ChoiceField(choices=choices_as_callable)
     907
     908        choices = [('J', 'John'), ('P', 'Paul')]
     909        form = ChoiceFieldForm()
     910        self.assertTrue("John" in form.as_p())
     911
     912        choices = [('M', 'Marie'),]
     913        form = ChoiceFieldForm()
     914        self.assertTrue("Marie" in form.as_p())
     915
    896916    # NullBooleanField ############################################################
    897917
    898918    def test_nullbooleanfield_1(self):
Back to Top