diff --git a/django/newforms/forms.py b/django/newforms/forms.py
index f58ce2c563..bf05e38fc2 100644
--- a/django/newforms/forms.py
+++ b/django/newforms/forms.py
@@ -36,6 +36,7 @@ class Form(object):
__metaclass__ = DeclarativeFieldsMetaclass
def __init__(self, data=None, auto_id=False): # TODO: prefix stuff
+ self.ignore_errors = data is None
self.data = data or {}
self.auto_id = auto_id
self.clean_data = None # Stores the data after clean() has been called.
@@ -65,22 +66,13 @@ class Form(object):
def is_valid(self):
"""
- Returns True if the form has no errors. Otherwise, False. This exists
- solely for convenience, so client code can use positive logic rather
- than confusing negative logic ("if not form.errors").
+ Returns True if the form has no errors. Otherwise, False. If errors are
+ being ignored, returns False.
"""
- return not bool(self.errors)
+ return not self.ignore_errors and not bool(self.errors)
def as_table(self):
"Returns this form rendered as HTML
s -- excluding the
."
- return u'\n'.join([u'
%s:
%s
' % (pretty_name(name), BoundField(self, field, name)) for name, field in self.fields.items()])
-
- def as_ul(self):
- "Returns this form rendered as HTML
s -- excluding the
."
- return u'\n'.join([u'
%s: %s
' % (pretty_name(name), BoundField(self, field, name)) for name, field in self.fields.items()])
-
- def as_table_with_errors(self):
- "Returns this form rendered as HTML
s, with errors."
output = []
if self.errors.get(NON_FIELD_ERRORS):
# Errors not corresponding to a particular field are displayed at the top.
@@ -92,8 +84,8 @@ class Form(object):
output.append(u'
%s:
%s
' % (pretty_name(name), bf))
return u'\n'.join(output)
- def as_ul_with_errors(self):
- "Returns this form rendered as HTML
s, with errors."
+ def as_ul(self):
+ "Returns this form rendered as HTML
s -- excluding the
."
output = []
if self.errors.get(NON_FIELD_ERRORS):
# Errors not corresponding to a particular field are displayed at the top.
@@ -113,6 +105,9 @@ class Form(object):
"""
self.clean_data = {}
errors = ErrorDict()
+ if self.ignore_errors: # Stop further processing.
+ self.__errors = errors
+ return
for name, field in self.fields.items():
value = self.data.get(name, None)
try:
diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py
index 3d7543badd..8de27dee51 100644
--- a/tests/regressiontests/forms/tests.py
+++ b/tests/regressiontests/forms/tests.py
@@ -1137,31 +1137,8 @@ u''
... first_name = CharField()
... last_name = CharField()
... birthday = DateField()
->>> p = Person()
->>> print p
-
First name:
-
Last name:
-
Birthday:
->>> print p.as_table()
-
First name:
-
Last name:
-
Birthday:
->>> print p.as_ul()
-
First name:
-
Last name:
-
Birthday:
->>> print p.as_table_with_errors()
-
This field is required.
-
First name:
-
This field is required.
-
Last name:
-
This field is required.
-
Birthday:
->>> print p.as_ul_with_errors()
-
This field is required.
First name:
-
This field is required.
Last name:
-
This field is required.
Birthday:
+Pass a dictionary to a Form's __init__().
>>> p = Person({'first_name': u'John', 'last_name': u'Lennon', 'birthday': u'1940-10-9'})
>>> p.errors
{}
@@ -1189,12 +1166,58 @@ u''
Last name:
Birthday:
+Empty dictionaries are valid, too.
+>>> p = Person({})
+>>> p.errors
+{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']}
+>>> p.is_valid()
+False
+>>> print p
+
This field is required.
+
First name:
+
This field is required.
+
Last name:
+
This field is required.
+
Birthday:
+>>> print p.as_table()
+
This field is required.
+
First name:
+
This field is required.
+
Last name:
+
This field is required.
+
Birthday:
+>>> print p.as_ul()
+
This field is required.
First name:
+
This field is required.
Last name:
+
This field is required.
Birthday:
+
+If you don't pass any values to the Form's __init__(), or if you pass None,
+the Form won't do any validation. Form.errors will be an empty dictionary *but*
+Form.is_valid() will return False.
+>>> p = Person()
+>>> p.errors
+{}
+>>> p.is_valid()
+False
+>>> print p
+
First name:
+
Last name:
+
Birthday:
+>>> print p.as_table()
+
First name:
+
Last name:
+
Birthday:
+>>> print p.as_ul()
+
First name:
+
Last name:
+
Birthday:
+
Unicode values are handled properly.
->>> p = Person({'first_name': u'John', 'last_name': u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111'})
+>>> p = Person({'first_name': u'John', 'last_name': u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111', 'birthday': '1940-10-9'})
>>> p.as_table()
-u'
First name:
\n
Last name:
\n
Birthday:
'
+u'
First name:
\n
Last name:
\n
Birthday:
'
>>> p.as_ul()
-u'
First name:
\n
Last name:
\n
Birthday:
'
+u'
First name:
\n
Last name:
\n
Birthday:
'
>>> p = Person({'last_name': u'Lennon'})
>>> p.errors
@@ -1375,7 +1398,10 @@ MultipleChoiceField is a special case, as its data is required to be a list:
There are a couple of ways to do multiple-field validation. If you want the
validation message to be associated with a particular field, implement the
clean_XXX() method on the Form, where XXX is the field name. As in
-Field.clean(), the clean_XXX() method should return the cleaned value:
+Field.clean(), the clean_XXX() method should return the cleaned value. In the
+clean_XXX() method, you have access to self.clean_data, which is a dictionary
+of all the data that has been cleaned *so far*, in order by the fields,
+including the current field (e.g., the field XXX if you're in clean_XXX()).
>>> class UserRegistration(Form):
... username = CharField(max_length=10)
... password1 = CharField(widget=PasswordInput)
@@ -1386,6 +1412,9 @@ Field.clean(), the clean_XXX() method should return the cleaned value:
... return self.clean_data['password2']
>>> f = UserRegistration()
>>> f.errors
+{}
+>>> f = UserRegistration({})
+>>> f.errors
{'username': [u'This field is required.'], 'password1': [u'This field is required.'], 'password2': [u'This field is required.']}
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'})
>>> f.errors
@@ -1399,8 +1428,10 @@ Field.clean(), the clean_XXX() method should return the cleaned value:
Another way of doing multiple-field validation is by implementing the
Form's clean() method. If you do this, any ValidationError raised by that
method will not be associated with a particular field; it will have a
-special-case association with the field named '__all__'. Note that
-Form.clean() still needs to return a dictionary of all clean data:
+special-case association with the field named '__all__'.
+Note that in Form.clean(), you have access to self.clean_data, a dictionary of
+all the fields/values that have *not* raised a ValidationError. Also note
+Form.clean() is required to return a dictionary of all clean data.
>>> class UserRegistration(Form):
... username = CharField(max_length=10)
... password1 = CharField(widget=PasswordInput)
@@ -1410,9 +1441,15 @@ Form.clean() still needs to return a dictionary of all clean data:
... raise ValidationError(u'Please make sure your passwords match.')
... return self.clean_data
>>> f = UserRegistration()
+>>> f.errors
+{}
+>>> f = UserRegistration({})
>>> print f.as_table()
+
This field is required.
Username:
+
This field is required.
Password1:
+
This field is required.
Password2:
>>> f.errors
{'username': [u'This field is required.'], 'password1': [u'This field is required.'], 'password2': [u'This field is required.']}
@@ -1420,15 +1457,11 @@ Form.clean() still needs to return a dictionary of all clean data:
>>> f.errors
{'__all__': [u'Please make sure your passwords match.']}
>>> print f.as_table()
-