Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #3026 -- newforms: Form class now suppresses validation and err…

…ors if no data (or None) is passed in. Validation still happens if you pass in an empty dictionary. Also updated unit tests. Thanks, SmileyChris

git-svn-id: http://code.djangoproject.com/svn/django/trunk@4117 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 126e0ec0c3096ae5c631b00b29ff9e9f01ca15c2 1 parent 682e435
Adrian Holovaty authored November 27, 2006
23  django/newforms/forms.py
@@ -36,6 +36,7 @@ class Form(object):
36 36
     __metaclass__ = DeclarativeFieldsMetaclass
37 37
 
38 38
     def __init__(self, data=None, auto_id=False): # TODO: prefix stuff
  39
+        self.ignore_errors = data is None
39 40
         self.data = data or {}
40 41
         self.auto_id = auto_id
41 42
         self.clean_data = None # Stores the data after clean() has been called.
@@ -65,22 +66,13 @@ def _errors(self):
65 66
 
66 67
     def is_valid(self):
67 68
         """
68  
-        Returns True if the form has no errors. Otherwise, False. This exists
69  
-        solely for convenience, so client code can use positive logic rather
70  
-        than confusing negative logic ("if not form.errors").
  69
+        Returns True if the form has no errors. Otherwise, False. If errors are
  70
+        being ignored, returns False.
71 71
         """
72  
-        return not bool(self.errors)
  72
+        return not self.ignore_errors and not bool(self.errors)
73 73
 
74 74
     def as_table(self):
75 75
         "Returns this form rendered as HTML <tr>s -- excluding the <table></table>."
76  
-        return u'\n'.join([u'<tr><td>%s:</td><td>%s</td></tr>' % (pretty_name(name), BoundField(self, field, name)) for name, field in self.fields.items()])
77  
-
78  
-    def as_ul(self):
79  
-        "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
80  
-        return u'\n'.join([u'<li>%s: %s</li>' % (pretty_name(name), BoundField(self, field, name)) for name, field in self.fields.items()])
81  
-
82  
-    def as_table_with_errors(self):
83  
-        "Returns this form rendered as HTML <tr>s, with errors."
84 76
         output = []
85 77
         if self.errors.get(NON_FIELD_ERRORS):
86 78
             # Errors not corresponding to a particular field are displayed at the top.
@@ -92,8 +84,8 @@ def as_table_with_errors(self):
92 84
             output.append(u'<tr><td>%s:</td><td>%s</td></tr>' % (pretty_name(name), bf))
93 85
         return u'\n'.join(output)
94 86
 
95  
-    def as_ul_with_errors(self):
96  
-        "Returns this form rendered as HTML <li>s, with errors."
  87
+    def as_ul(self):
  88
+        "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
97 89
         output = []
98 90
         if self.errors.get(NON_FIELD_ERRORS):
99 91
             # Errors not corresponding to a particular field are displayed at the top.
@@ -113,6 +105,9 @@ def full_clean(self):
113 105
         """
114 106
         self.clean_data = {}
115 107
         errors = ErrorDict()
  108
+        if self.ignore_errors: # Stop further processing.
  109
+            self.__errors = errors
  110
+            return
116 111
         for name, field in self.fields.items():
117 112
             value = self.data.get(name, None)
118 113
             try:
152  tests/regressiontests/forms/tests.py
@@ -1137,31 +1137,8 @@
1137 1137
 ...     first_name = CharField()
1138 1138
 ...     last_name = CharField()
1139 1139
 ...     birthday = DateField()
1140  
->>> p = Person()
1141  
->>> print p
1142  
-<tr><td>First name:</td><td><input type="text" name="first_name" /></td></tr>
1143  
-<tr><td>Last name:</td><td><input type="text" name="last_name" /></td></tr>
1144  
-<tr><td>Birthday:</td><td><input type="text" name="birthday" /></td></tr>
1145  
->>> print p.as_table()
1146  
-<tr><td>First name:</td><td><input type="text" name="first_name" /></td></tr>
1147  
-<tr><td>Last name:</td><td><input type="text" name="last_name" /></td></tr>
1148  
-<tr><td>Birthday:</td><td><input type="text" name="birthday" /></td></tr>
1149  
->>> print p.as_ul()
1150  
-<li>First name: <input type="text" name="first_name" /></li>
1151  
-<li>Last name: <input type="text" name="last_name" /></li>
1152  
-<li>Birthday: <input type="text" name="birthday" /></li>
1153  
->>> print p.as_table_with_errors()
1154  
-<tr><td colspan="2"><ul><li>This field is required.</li></ul></td></tr>
1155  
-<tr><td>First name:</td><td><input type="text" name="first_name" /></td></tr>
1156  
-<tr><td colspan="2"><ul><li>This field is required.</li></ul></td></tr>
1157  
-<tr><td>Last name:</td><td><input type="text" name="last_name" /></td></tr>
1158  
-<tr><td colspan="2"><ul><li>This field is required.</li></ul></td></tr>
1159  
-<tr><td>Birthday:</td><td><input type="text" name="birthday" /></td></tr>
1160  
->>> print p.as_ul_with_errors()
1161  
-<li><ul><li>This field is required.</li></ul>First name: <input type="text" name="first_name" /></li>
1162  
-<li><ul><li>This field is required.</li></ul>Last name: <input type="text" name="last_name" /></li>
1163  
-<li><ul><li>This field is required.</li></ul>Birthday: <input type="text" name="birthday" /></li>
1164 1140
 
  1141
+Pass a dictionary to a Form's __init__().
1165 1142
 >>> p = Person({'first_name': u'John', 'last_name': u'Lennon', 'birthday': u'1940-10-9'})
1166 1143
 >>> p.errors
1167 1144
 {}
@@ -1189,12 +1166,58 @@
1189 1166
 <tr><td>Last name:</td><td><input type="text" name="last_name" value="Lennon" /></td></tr>
1190 1167
 <tr><td>Birthday:</td><td><input type="text" name="birthday" value="1940-10-9" /></td></tr>
1191 1168
 
  1169
+Empty dictionaries are valid, too.
  1170
+>>> p = Person({})
  1171
+>>> p.errors
  1172
+{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']}
  1173
+>>> p.is_valid()
  1174
+False
  1175
+>>> print p
  1176
+<tr><td colspan="2"><ul><li>This field is required.</li></ul></td></tr>
  1177
+<tr><td>First name:</td><td><input type="text" name="first_name" /></td></tr>
  1178
+<tr><td colspan="2"><ul><li>This field is required.</li></ul></td></tr>
  1179
+<tr><td>Last name:</td><td><input type="text" name="last_name" /></td></tr>
  1180
+<tr><td colspan="2"><ul><li>This field is required.</li></ul></td></tr>
  1181
+<tr><td>Birthday:</td><td><input type="text" name="birthday" /></td></tr>
  1182
+>>> print p.as_table()
  1183
+<tr><td colspan="2"><ul><li>This field is required.</li></ul></td></tr>
  1184
+<tr><td>First name:</td><td><input type="text" name="first_name" /></td></tr>
  1185
+<tr><td colspan="2"><ul><li>This field is required.</li></ul></td></tr>
  1186
+<tr><td>Last name:</td><td><input type="text" name="last_name" /></td></tr>
  1187
+<tr><td colspan="2"><ul><li>This field is required.</li></ul></td></tr>
  1188
+<tr><td>Birthday:</td><td><input type="text" name="birthday" /></td></tr>
  1189
+>>> print p.as_ul()
  1190
+<li><ul><li>This field is required.</li></ul>First name: <input type="text" name="first_name" /></li>
  1191
+<li><ul><li>This field is required.</li></ul>Last name: <input type="text" name="last_name" /></li>
  1192
+<li><ul><li>This field is required.</li></ul>Birthday: <input type="text" name="birthday" /></li>
  1193
+
  1194
+If you don't pass any values to the Form's __init__(), or if you pass None,
  1195
+the Form won't do any validation. Form.errors will be an empty dictionary *but*
  1196
+Form.is_valid() will return False.
  1197
+>>> p = Person()
  1198
+>>> p.errors
  1199
+{}
  1200
+>>> p.is_valid()
  1201
+False
  1202
+>>> print p
  1203
+<tr><td>First name:</td><td><input type="text" name="first_name" /></td></tr>
  1204
+<tr><td>Last name:</td><td><input type="text" name="last_name" /></td></tr>
  1205
+<tr><td>Birthday:</td><td><input type="text" name="birthday" /></td></tr>
  1206
+>>> print p.as_table()
  1207
+<tr><td>First name:</td><td><input type="text" name="first_name" /></td></tr>
  1208
+<tr><td>Last name:</td><td><input type="text" name="last_name" /></td></tr>
  1209
+<tr><td>Birthday:</td><td><input type="text" name="birthday" /></td></tr>
  1210
+>>> print p.as_ul()
  1211
+<li>First name: <input type="text" name="first_name" /></li>
  1212
+<li>Last name: <input type="text" name="last_name" /></li>
  1213
+<li>Birthday: <input type="text" name="birthday" /></li>
  1214
+
1192 1215
 Unicode values are handled properly.
1193  
->>> p = Person({'first_name': u'John', 'last_name': u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111'})
  1216
+>>> p = Person({'first_name': u'John', 'last_name': u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111', 'birthday': '1940-10-9'})
1194 1217
 >>> p.as_table()
1195  
-u'<tr><td>First name:</td><td><input type="text" name="first_name" value="John" /></td></tr>\n<tr><td>Last name:</td><td><input type="text" name="last_name" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /></td></tr>\n<tr><td>Birthday:</td><td><input type="text" name="birthday" /></td></tr>'
  1218
+u'<tr><td>First name:</td><td><input type="text" name="first_name" value="John" /></td></tr>\n<tr><td>Last name:</td><td><input type="text" name="last_name" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /></td></tr>\n<tr><td>Birthday:</td><td><input type="text" name="birthday" value="1940-10-9" /></td></tr>'
1196 1219
 >>> p.as_ul()
1197  
-u'<li>First name: <input type="text" name="first_name" value="John" /></li>\n<li>Last name: <input type="text" name="last_name" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /></li>\n<li>Birthday: <input type="text" name="birthday" /></li>'
  1220
+u'<li>First name: <input type="text" name="first_name" value="John" /></li>\n<li>Last name: <input type="text" name="last_name" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /></li>\n<li>Birthday: <input type="text" name="birthday" value="1940-10-9" /></li>'
1198 1221
 
1199 1222
 >>> p = Person({'last_name': u'Lennon'})
1200 1223
 >>> p.errors
@@ -1375,7 +1398,10 @@
1375 1398
 There are a couple of ways to do multiple-field validation. If you want the
1376 1399
 validation message to be associated with a particular field, implement the
1377 1400
 clean_XXX() method on the Form, where XXX is the field name. As in
1378  
-Field.clean(), the clean_XXX() method should return the cleaned value:
  1401
+Field.clean(), the clean_XXX() method should return the cleaned value. In the
  1402
+clean_XXX() method, you have access to self.clean_data, which is a dictionary
  1403
+of all the data that has been cleaned *so far*, in order by the fields,
  1404
+including the current field (e.g., the field XXX if you're in clean_XXX()).
1379 1405
 >>> class UserRegistration(Form):
1380 1406
 ...    username = CharField(max_length=10)
1381 1407
 ...    password1 = CharField(widget=PasswordInput)
@@ -1386,6 +1412,9 @@
1386 1412
 ...        return self.clean_data['password2']
1387 1413
 >>> f = UserRegistration()
1388 1414
 >>> f.errors
  1415
+{}
  1416
+>>> f = UserRegistration({})
  1417
+>>> f.errors
1389 1418
 {'username': [u'This field is required.'], 'password1': [u'This field is required.'], 'password2': [u'This field is required.']}
1390 1419
 >>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'})
1391 1420
 >>> f.errors
@@ -1399,8 +1428,10 @@
1399 1428
 Another way of doing multiple-field validation is by implementing the
1400 1429
 Form's clean() method. If you do this, any ValidationError raised by that
1401 1430
 method will not be associated with a particular field; it will have a
1402  
-special-case association with the field named '__all__'. Note that
1403  
-Form.clean() still needs to return a dictionary of all clean data:
  1431
+special-case association with the field named '__all__'.
  1432
+Note that in Form.clean(), you have access to self.clean_data, a dictionary of
  1433
+all the fields/values that have *not* raised a ValidationError. Also note
  1434
+Form.clean() is required to return a dictionary of all clean data.
1404 1435
 >>> class UserRegistration(Form):
1405 1436
 ...    username = CharField(max_length=10)
1406 1437
 ...    password1 = CharField(widget=PasswordInput)
@@ -1410,9 +1441,15 @@
1410 1441
 ...            raise ValidationError(u'Please make sure your passwords match.')
1411 1442
 ...        return self.clean_data
1412 1443
 >>> f = UserRegistration()
  1444
+>>> f.errors
  1445
+{}
  1446
+>>> f = UserRegistration({})
1413 1447
 >>> print f.as_table()
  1448
+<tr><td colspan="2"><ul><li>This field is required.</li></ul></td></tr>
1414 1449
 <tr><td>Username:</td><td><input type="text" name="username" /></td></tr>
  1450
+<tr><td colspan="2"><ul><li>This field is required.</li></ul></td></tr>
1415 1451
 <tr><td>Password1:</td><td><input type="password" name="password1" /></td></tr>
  1452
+<tr><td colspan="2"><ul><li>This field is required.</li></ul></td></tr>
1416 1453
 <tr><td>Password2:</td><td><input type="password" name="password2" /></td></tr>
1417 1454
 >>> f.errors
1418 1455
 {'username': [u'This field is required.'], 'password1': [u'This field is required.'], 'password2': [u'This field is required.']}
@@ -1420,15 +1457,11 @@
1420 1457
 >>> f.errors
1421 1458
 {'__all__': [u'Please make sure your passwords match.']}
1422 1459
 >>> print f.as_table()
1423  
-<tr><td>Username:</td><td><input type="text" name="username" value="adrian" /></td></tr>
1424  
-<tr><td>Password1:</td><td><input type="password" name="password1" value="foo" /></td></tr>
1425  
-<tr><td>Password2:</td><td><input type="password" name="password2" value="bar" /></td></tr>
1426  
->>> print f.as_table_with_errors()
1427 1460
 <tr><td colspan="2"><ul><li>Please make sure your passwords match.</li></ul></td></tr>
1428 1461
 <tr><td>Username:</td><td><input type="text" name="username" value="adrian" /></td></tr>
1429 1462
 <tr><td>Password1:</td><td><input type="password" name="password1" value="foo" /></td></tr>
1430 1463
 <tr><td>Password2:</td><td><input type="password" name="password2" value="bar" /></td></tr>
1431  
->>> print f.as_ul_with_errors()
  1464
+>>> print f.as_ul()
1432 1465
 <li><ul><li>Please make sure your passwords match.</li></ul></li>
1433 1466
 <li>Username: <input type="text" name="username" value="adrian" /></li>
1434 1467
 <li>Password1: <input type="password" name="password1" value="foo" /></li>
@@ -1486,6 +1519,55 @@
1486 1519
 <tr><td>Field12:</td><td><input type="text" name="field12" /></td></tr>
1487 1520
 <tr><td>Field13:</td><td><input type="text" name="field13" /></td></tr>
1488 1521
 <tr><td>Field14:</td><td><input type="text" name="field14" /></td></tr>
  1522
+
  1523
+# Sample form processing (as if in a view) ####################################
  1524
+
  1525
+>>> from django.template import Template, Context
  1526
+>>> class UserRegistration(Form):
  1527
+...    username = CharField(max_length=10)
  1528
+...    password1 = CharField(widget=PasswordInput)
  1529
+...    password2 = CharField(widget=PasswordInput)
  1530
+...    def clean(self):
  1531
+...        if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']:
  1532
+...            raise ValidationError(u'Please make sure your passwords match.')
  1533
+...        return self.clean_data
  1534
+>>> def my_function(method, post_data):
  1535
+...     if method == 'POST':
  1536
+...         form = UserRegistration(post_data)
  1537
+...     else:
  1538
+...         form = UserRegistration()
  1539
+...     if form.is_valid():
  1540
+...         return 'VALID'
  1541
+...     t = Template('<form action="" method="post">\n<table>\n{{ form }}\n</table>\n<input type="submit" />\n</form>')
  1542
+...     return t.render(Context({'form': form}))
  1543
+
  1544
+Case 1: GET (an empty form, with no errors).
  1545
+>>> print my_function('GET', {})
  1546
+<form action="" method="post">
  1547
+<table>
  1548
+<tr><td>Username:</td><td><input type="text" name="username" /></td></tr>
  1549
+<tr><td>Password1:</td><td><input type="password" name="password1" /></td></tr>
  1550
+<tr><td>Password2:</td><td><input type="password" name="password2" /></td></tr>
  1551
+</table>
  1552
+<input type="submit" />
  1553
+</form>
  1554
+
  1555
+Case 2: POST with erroneous data (a redisplayed form, with errors).
  1556
+>>> print my_function('POST', {'username': 'this-is-a-long-username', 'password1': 'foo', 'password2': 'bar'})
  1557
+<form action="" method="post">
  1558
+<table>
  1559
+<tr><td colspan="2"><ul><li>Please make sure your passwords match.</li></ul></td></tr>
  1560
+<tr><td colspan="2"><ul><li>Ensure this value has at most 10 characters.</li></ul></td></tr>
  1561
+<tr><td>Username:</td><td><input type="text" name="username" value="this-is-a-long-username" /></td></tr>
  1562
+<tr><td>Password1:</td><td><input type="password" name="password1" value="foo" /></td></tr>
  1563
+<tr><td>Password2:</td><td><input type="password" name="password2" value="bar" /></td></tr>
  1564
+</table>
  1565
+<input type="submit" />
  1566
+</form>
  1567
+
  1568
+Case 3: POST with valid data (the success message).
  1569
+>>> print my_function('POST', {'username': 'adrian', 'password1': 'secret', 'password2': 'secret'})
  1570
+VALID
1489 1571
 """
1490 1572
 
1491 1573
 if __name__ == "__main__":

0 notes on commit 126e0ec

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