diff options
| author | Adrian Holovaty <adrian@holovaty.com> | 2006-11-02 03:16:12 +0000 |
|---|---|---|
| committer | Adrian Holovaty <adrian@holovaty.com> | 2006-11-02 03:16:12 +0000 |
| commit | 6645d1fe48868814e4c73056b68be5c3861ed2d0 (patch) | |
| tree | 9df533c7a2eb467eb75151df95a8462ca1dc6ab9 | |
| parent | 36786d28f55e307e0acc6f1c0f432368bde44426 (diff) | |
Added ChoiceField, MultipleChoiceField to django.newforms
git-svn-id: http://code.djangoproject.com/svn/django/trunk@3959 bcc190cf-cafb-0310-a4f2-bffc1f526a37
| -rw-r--r-- | django/newforms/__init__.py | 1 | ||||
| -rw-r--r-- | django/newforms/fields.py | 56 | ||||
| -rw-r--r-- | tests/regressiontests/forms/tests.py | 100 |
3 files changed, 155 insertions, 2 deletions
diff --git a/django/newforms/__init__.py b/django/newforms/__init__.py index f0eca9a950..9d12c55ae9 100644 --- a/django/newforms/__init__.py +++ b/django/newforms/__init__.py @@ -3,7 +3,6 @@ Django validation and HTML form handling. TODO: Validation not tied to a particular field - <select> and validation of lists Default value for field Field labels Nestable Forms diff --git a/django/newforms/fields.py b/django/newforms/fields.py index 29ede3a0c1..83867dccc3 100644 --- a/django/newforms/fields.py +++ b/django/newforms/fields.py @@ -3,7 +3,7 @@ Field classes """ from util import ValidationError, DEFAULT_ENCODING -from widgets import TextInput, CheckboxInput +from widgets import TextInput, CheckboxInput, Select, SelectMultiple import datetime import re import time @@ -13,11 +13,17 @@ __all__ = ( 'DEFAULT_DATE_INPUT_FORMATS', 'DateField', 'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField', 'RegexField', 'EmailField', 'URLField', 'BooleanField', + 'ChoiceField', 'MultipleChoiceField', ) # These values, if given to to_python(), will trigger the self.required check. EMPTY_VALUES = (None, '') +try: + set # Only available in Python 2.4+ +except NameError: + from sets import Set as set # Python 2.3 fallback + class Field(object): widget = TextInput # Default widget to use when rendering this type of Field. @@ -205,3 +211,51 @@ class BooleanField(Field): "Returns a Python boolean object." Field.to_python(self, value) return bool(value) + +class ChoiceField(Field): + def __init__(self, choices=(), required=True, widget=Select): + if isinstance(widget, type): + widget = widget(choices=choices) + Field.__init__(self, required, widget) + self.choices = choices + + def to_python(self, value): + """ + Validates that the input is in self.choices. + """ + value = Field.to_python(self, value) + if value in EMPTY_VALUES: value = u'' + if not isinstance(value, basestring): + value = unicode(str(value), DEFAULT_ENCODING) + elif not isinstance(value, unicode): + value = unicode(value, DEFAULT_ENCODING) + valid_values = set([str(k) for k, v in self.choices]) + if value not in valid_values: + raise ValidationError(u'Select a valid choice. %s is not one of the available choices.' % value) + return value + +class MultipleChoiceField(ChoiceField): + def __init__(self, choices=(), required=True, widget=SelectMultiple): + ChoiceField.__init__(self, choices, required, widget) + + def to_python(self, value): + """ + Validates that the input is a list or tuple. + """ + if not isinstance(value, (list, tuple)): + raise ValidationError(u'Enter a list of values.') + if self.required and not value: + raise ValidationError(u'This field is required.') + new_value = [] + for val in value: + if not isinstance(val, basestring): + value = unicode(str(val), DEFAULT_ENCODING) + elif not isinstance(val, unicode): + value = unicode(val, DEFAULT_ENCODING) + new_value.append(value) + # Validate that each value in the value list is in self.choices. + valid_values = set([k for k, v in self.choices]) + for val in new_value: + if val not in valid_values: + raise ValidationError(u'Select a valid choice. %s is not one of the available choices.' % val) + return new_value diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py index 49907cd9e9..92f66af213 100644 --- a/tests/regressiontests/forms/tests.py +++ b/tests/regressiontests/forms/tests.py @@ -680,6 +680,64 @@ False >>> f.to_python('Django rocks') True +# ChoiceField ################################################################# + +>>> f = ChoiceField(choices=[('1', '1'), ('2', '2')]) +>>> f.to_python(1) +u'1' +>>> f.to_python('1') +u'1' +>>> f.to_python(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.to_python('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.to_python('3') +Traceback (most recent call last): +... +ValidationError: [u'Select a valid choice. 3 is not one of the available choices.'] + +>>> f = ChoiceField(choices=[('J', 'John'), ('P', 'Paul')]) +>>> f.to_python('J') +u'J' +>>> f.to_python('John') +Traceback (most recent call last): +... +ValidationError: [u'Select a valid choice. John is not one of the available choices.'] + +# MultipleChoiceField ######################################################### + +>>> f = MultipleChoiceField(choices=[('1', '1'), ('2', '2')]) +>>> f.to_python([1]) +[u'1'] +>>> f.to_python(['1']) +[u'1'] +>>> f.to_python(['1', '2']) +[u'1', u'2'] +>>> f.to_python([1, '2']) +[u'1', u'2'] +>>> f.to_python((1, '2')) +[u'1', u'2'] +>>> f.to_python('hello') +Traceback (most recent call last): +... +ValidationError: [u'Enter a list of values.'] +>>> f.to_python([]) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.to_python(()) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.to_python(['3']) +Traceback (most recent call last): +... +ValidationError: [u'Select a valid choice. 3 is not one of the available choices.'] + # Form ######################################################################## >>> class Person(Form): @@ -787,6 +845,48 @@ u'<textarea name="subject">Hello</textarea>' >>> f['message'].as_text() u'<input type="text" name="message" value="I love you." />' +For a form with a <select>, use ChoiceField: +>>> class FrameworkForm(Form): +... name = CharField() +... language = ChoiceField(choices=[('P', 'Python'), ('J', 'Java')]) +>>> f = FrameworkForm() +>>> print f['language'] +<select name="language"> +<option value="P">Python</option> +<option value="J">Java</option> +</select> +>>> f = FrameworkForm({'name': 'Django', 'language': 'P'}) +>>> print f['language'] +<select name="language"> +<option value="P" selected="selected">Python</option> +<option value="J">Java</option> +</select> + +MultipleChoiceField is a special case, as its data is required to be a list: +>>> class SongForm(Form): +... name = CharField() +... composers = MultipleChoiceField() +>>> f = SongForm() +>>> print f['composers'] +<select multiple="multiple" name="composers"> +</select> +>>> class SongForm(Form): +... name = CharField() +... composers = MultipleChoiceField(choices=[('J', 'John Lennon'), ('P', 'Paul McCartney')]) +>>> f = SongForm() +>>> print f['composers'] +<select multiple="multiple" name="composers"> +<option value="J">John Lennon</option> +<option value="P">Paul McCartney</option> +</select> +>>> f = SongForm({'name': 'Yesterday', 'composers': ['P']}) +>>> print f['name'] +<input type="text" name="name" value="Yesterday" /> +>>> print f['composers'] +<select multiple="multiple" name="composers"> +<option value="J">John Lennon</option> +<option value="P" selected="selected">Paul McCartney</option> +</select> """ if __name__ == "__main__": |
