Index: django/newforms/forms.py
===================================================================
--- django/newforms/forms.py	(revision 6917)
+++ django/newforms/forms.py	(working copy)
@@ -195,8 +195,6 @@
             self.cleaned_data = self.clean()
         except ValidationError, e:
             self._errors[NON_FIELD_ERRORS] = e.messages
-        if self._errors:
-            delattr(self, 'cleaned_data')
 
     def clean(self):
         """
@@ -293,6 +291,20 @@
         """
         return self.field.widget.value_from_datadict(self.form.data, self.form.files, self.html_name)
     data = property(_data)
+    
+    def _cleaned_data(self):
+        """
+        Returns the cleaned_data for this BoundField, or raises KeyError if this field
+        lacks an entry in cleaned_data.
+        
+        Hint: If self.errors evaluates to False, we know this won't throw a
+        KeyError (otherwise, it will).
+        """
+        try:
+            return self.form.cleaned_data[self.name]
+        except KeyError:
+            raise KeyError, "Field '%s' did not provide cleaned data." % self.name
+    cleaned_data = property(_cleaned_data)
 
     def label_tag(self, contents=None, attrs=None):
         """
Index: tests/modeltests/model_forms/models.py
===================================================================
--- tests/modeltests/model_forms/models.py	(revision 6917)
+++ tests/modeltests/model_forms/models.py	(working copy)
@@ -235,9 +235,7 @@
 >>> f.errors
 {'name': [u'This field is required.'], 'slug': [u'This field is required.']}
 >>> f.cleaned_data
-Traceback (most recent call last):
-...
-AttributeError: 'CategoryForm' object has no attribute 'cleaned_data'
+{'url': u'foo'}
 >>> f.save()
 Traceback (most recent call last):
 ...
Index: tests/regressiontests/forms/extra.py
===================================================================
--- tests/regressiontests/forms/extra.py	(revision 6917)
+++ tests/regressiontests/forms/extra.py	(working copy)
@@ -396,4 +396,34 @@
 >>> FormWithImage().is_multipart()
 True
 
+###################################
+# Test cleaned_data on BoundField #
+###################################
+
+>>> class BandMemberForm(Form):
+...     name = CharField(max_length=20)
+...     plays_drums = BooleanField()
+
+>>> f = BandMemberForm({'name': 'Ringo', 'plays_drums': 'True'})
+>>> f.is_valid()
+True
+>>> f['name'].data
+'Ringo'
+>>> f['name'].cleaned_data
+u'Ringo'
+>>> f['plays_drums'].data
+'True'
+>>> f['plays_drums'].cleaned_data
+True
+
+>>> f = BandMemberForm({'plays_drums': 'True'})
+>>> f.is_valid()
+False
+>>> f['name'].data
+>>> f['name'].cleaned_data
+Traceback (most recent call last):
+...
+KeyError: "Field 'name' did not provide cleaned data."
+>>> f['plays_drums'].cleaned_data
+True
 """
Index: tests/regressiontests/forms/forms.py
===================================================================
--- tests/regressiontests/forms/forms.py	(revision 6917)
+++ tests/regressiontests/forms/forms.py	(working copy)
@@ -73,9 +73,7 @@
 >>> p.is_valid()
 False
 >>> p.cleaned_data
-Traceback (most recent call last):
-...
-AttributeError: 'Person' object has no attribute 'cleaned_data'
+{}
 >>> print p
 <tr><th><label for="id_first_name">First name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="first_name" id="id_first_name" /></td></tr>
 <tr><th><label for="id_last_name">Last name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="last_name" id="id_last_name" /></td></tr>
@@ -149,9 +147,7 @@
 * birthday
   * This field is required.
 >>> p.cleaned_data
-Traceback (most recent call last):
-...
-AttributeError: 'Person' object has no attribute 'cleaned_data'
+{'last_name': u'Lennon'}
 >>> p['first_name'].errors
 [u'This field is required.']
 >>> p['first_name'].errors.as_ul()
Index: docs/newforms.txt
===================================================================
--- docs/newforms.txt	(revision 6917)
+++ docs/newforms.txt	(working copy)
@@ -247,8 +247,9 @@
 always cleans the input into a Unicode string. We'll cover the encoding
 implications later in this document.
 
-If your data does *not* validate, your ``Form`` instance will not have a
-``cleaned_data`` attribute::
+If your data does *not* validate, your ``Form`` instance will still have a 
+``cleaned_data`` attribute.  It will contain cleaned data from fields that
+did validate::
 
     >>> data = {'subject': '',
     ...         'message': 'Hi there',
@@ -258,10 +259,12 @@
     >>> f.is_valid()
     False
     >>> f.cleaned_data
-    Traceback (most recent call last):
-    ...
-    AttributeError: 'ContactForm' object has no attribute 'cleaned_data'
+    {'cc_myself': True, 'message': u'Hi there'}
 
+**New in Django development version:** The ``cleaned_data`` attribute was
+previously absent if the data for a ``Form`` instance did not validate. This
+has changed.
+
 ``cleaned_data`` will always *only* contain a key for fields defined in the
 ``Form``, even if you pass extra data when you define the ``Form``. In this
 example, we pass a bunch of extra fields to the ``ContactForm`` constructor,
