Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

newforms: Implemented NullBooleanField and NullBooleanSelect

git-svn-id: http://code.djangoproject.com/svn/django/trunk@4411 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 788f8f74540b87f79fbfbe44a4b2f00502d66d8a 1 parent 85fe107
Adrian Holovaty authored January 24, 2007
14  django/newforms/fields.py
@@ -4,7 +4,7 @@
4 4
 
5 5
 from django.utils.translation import gettext
6 6
 from util import ErrorList, ValidationError, smart_unicode
7  
-from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, CheckboxInput, Select, SelectMultiple
  7
+from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple
8 8
 import datetime
9 9
 import re
10 10
 import time
@@ -15,7 +15,7 @@
15 15
     'DEFAULT_TIME_INPUT_FORMATS', 'TimeField',
16 16
     'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField',
17 17
     'RegexField', 'EmailField', 'URLField', 'BooleanField',
18  
-    'ChoiceField', 'MultipleChoiceField',
  18
+    'ChoiceField', 'NullBooleanField', 'MultipleChoiceField',
19 19
     'ComboField', 'MultiValueField',
20 20
     'SplitDateTimeField',
21 21
 )
@@ -317,6 +317,16 @@ def clean(self, value):
317 317
         super(BooleanField, self).clean(value)
318 318
         return bool(value)
319 319
 
  320
+class NullBooleanField(BooleanField):
  321
+    """
  322
+    A field whose valid values are None, True and False. Invalid values are
  323
+    cleaned to None.
  324
+    """
  325
+    widget = NullBooleanSelect
  326
+
  327
+    def clean(self, value):
  328
+        return {True: True, False: False}.get(value, None)
  329
+
320 330
 class ChoiceField(Field):
321 331
     def __init__(self, choices=(), required=True, widget=Select, label=None, initial=None):
322 332
         if isinstance(widget, type):
22  django/newforms/widgets.py
@@ -5,13 +5,14 @@
5 5
 __all__ = (
6 6
     'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'MultipleHiddenInput',
7 7
     'FileInput', 'Textarea', 'CheckboxInput',
8  
-    'Select', 'SelectMultiple', 'RadioSelect', 'CheckboxSelectMultiple',
  8
+    'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect', 'CheckboxSelectMultiple',
9 9
     'MultiWidget', 'SplitDateTimeWidget',
10 10
 )
11 11
 
12 12
 from util import flatatt, StrAndUnicode, smart_unicode
13 13
 from django.utils.datastructures import MultiValueDict
14 14
 from django.utils.html import escape
  15
+from django.utils.translation import gettext
15 16
 from itertools import chain
16 17
 
17 18
 try:
@@ -151,6 +152,25 @@ def render(self, name, value, attrs=None, choices=()):
151 152
         output.append(u'</select>')
152 153
         return u'\n'.join(output)
153 154
 
  155
+class NullBooleanSelect(Select):
  156
+    """
  157
+    A Select Widget intended to be used with NullBooleanField.
  158
+    """
  159
+    def __init__(self, attrs=None):
  160
+        choices = ((u'1', gettext('Unknown')), (u'2', gettext('Yes')), (u'3', gettext('No')))
  161
+        super(NullBooleanSelect, self).__init__(attrs, choices)
  162
+
  163
+    def render(self, name, value, attrs=None, choices=()):
  164
+        try:
  165
+            value = {True: u'2', False: u'3', u'2': u'2', u'3': u'3'}[value]
  166
+        except KeyError:
  167
+            value = u'1'
  168
+        return super(NullBooleanSelect, self).render(name, value, attrs, choices)
  169
+
  170
+    def value_from_datadict(self, data, name):
  171
+        value = data.get(name, None)
  172
+        return {u'2': True, u'3': False, True: True, False: False}.get(value, None)
  173
+
154 174
 class SelectMultiple(Widget):
155 175
     def __init__(self, attrs=None, choices=()):
156 176
         # choices can be any iterable
99  tests/regressiontests/forms/tests.py
@@ -336,6 +336,40 @@
336 336
 >>> w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])
337 337
 u'<select name="email">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" selected="selected">\u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</option>\n<option value="\u0107\u017e\u0161\u0111">abc\u0107\u017e\u0161\u0111</option>\n</select>'
338 338
 
  339
+# NullBooleanSelect Widget ####################################################
  340
+
  341
+>>> w = NullBooleanSelect()
  342
+>>> print w.render('is_cool', True)
  343
+<select name="is_cool">
  344
+<option value="1">Unknown</option>
  345
+<option value="2" selected="selected">Yes</option>
  346
+<option value="3">No</option>
  347
+</select>
  348
+>>> print w.render('is_cool', False)
  349
+<select name="is_cool">
  350
+<option value="1">Unknown</option>
  351
+<option value="2">Yes</option>
  352
+<option value="3" selected="selected">No</option>
  353
+</select>
  354
+>>> print w.render('is_cool', None)
  355
+<select name="is_cool">
  356
+<option value="1" selected="selected">Unknown</option>
  357
+<option value="2">Yes</option>
  358
+<option value="3">No</option>
  359
+</select>
  360
+>>> print w.render('is_cool', '2')
  361
+<select name="is_cool">
  362
+<option value="1">Unknown</option>
  363
+<option value="2" selected="selected">Yes</option>
  364
+<option value="3">No</option>
  365
+</select>
  366
+>>> print w.render('is_cool', '3')
  367
+<select name="is_cool">
  368
+<option value="1">Unknown</option>
  369
+<option value="2">Yes</option>
  370
+<option value="3" selected="selected">No</option>
  371
+</select>
  372
+
339 373
 # SelectMultiple Widget #######################################################
340 374
 
341 375
 >>> w = SelectMultiple()
@@ -1463,6 +1497,20 @@
1463 1497
 ...
1464 1498
 ValidationError: [u'Select a valid choice. John is not one of the available choices.']
1465 1499
 
  1500
+# NullBooleanField ############################################################
  1501
+
  1502
+>>> f = NullBooleanField()
  1503
+>>> f.clean('')
  1504
+>>> f.clean(True)
  1505
+True
  1506
+>>> f.clean(False)
  1507
+False
  1508
+>>> f.clean(None)
  1509
+>>> f.clean('1')
  1510
+>>> f.clean('2')
  1511
+>>> f.clean('3')
  1512
+>>> f.clean('hello')
  1513
+
1466 1514
 # MultipleChoiceField #########################################################
1467 1515
 
1468 1516
 >>> f = MultipleChoiceField(choices=[('1', '1'), ('2', '2')])
@@ -2601,6 +2649,57 @@
2601 2649
 >>> p.clean_data
2602 2650
 {'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
2603 2651
 
  2652
+# Forms with NullBooleanFields ################################################
  2653
+
  2654
+NullBooleanField is a bit of a special case because its presentation (widget)
  2655
+is different than its data. This is handled transparently, though.
  2656
+
  2657
+>>> class Person(Form):
  2658
+...     name = CharField()
  2659
+...     is_cool = NullBooleanField()
  2660
+>>> p = Person({'name': u'Joe'}, auto_id=False)
  2661
+>>> print p['is_cool']
  2662
+<select name="is_cool">
  2663
+<option value="1" selected="selected">Unknown</option>
  2664
+<option value="2">Yes</option>
  2665
+<option value="3">No</option>
  2666
+</select>
  2667
+>>> p = Person({'name': u'Joe', 'is_cool': u'1'}, auto_id=False)
  2668
+>>> print p['is_cool']
  2669
+<select name="is_cool">
  2670
+<option value="1" selected="selected">Unknown</option>
  2671
+<option value="2">Yes</option>
  2672
+<option value="3">No</option>
  2673
+</select>
  2674
+>>> p = Person({'name': u'Joe', 'is_cool': u'2'}, auto_id=False)
  2675
+>>> print p['is_cool']
  2676
+<select name="is_cool">
  2677
+<option value="1">Unknown</option>
  2678
+<option value="2" selected="selected">Yes</option>
  2679
+<option value="3">No</option>
  2680
+</select>
  2681
+>>> p = Person({'name': u'Joe', 'is_cool': u'3'}, auto_id=False)
  2682
+>>> print p['is_cool']
  2683
+<select name="is_cool">
  2684
+<option value="1">Unknown</option>
  2685
+<option value="2">Yes</option>
  2686
+<option value="3" selected="selected">No</option>
  2687
+</select>
  2688
+>>> p = Person({'name': u'Joe', 'is_cool': True}, auto_id=False)
  2689
+>>> print p['is_cool']
  2690
+<select name="is_cool">
  2691
+<option value="1">Unknown</option>
  2692
+<option value="2" selected="selected">Yes</option>
  2693
+<option value="3">No</option>
  2694
+</select>
  2695
+>>> p = Person({'name': u'Joe', 'is_cool': False}, auto_id=False)
  2696
+>>> print p['is_cool']
  2697
+<select name="is_cool">
  2698
+<option value="1">Unknown</option>
  2699
+<option value="2">Yes</option>
  2700
+<option value="3" selected="selected">No</option>
  2701
+</select>
  2702
+
2604 2703
 # Basic form processing in a view #############################################
2605 2704
 
2606 2705
 >>> from django.template import Template, Context

0 notes on commit 788f8f7

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