Index: django/newforms/formsets.py
===================================================================
--- django/newforms/formsets.py	(revision 6777)
+++ django/newforms/formsets.py	(working copy)
@@ -116,14 +116,19 @@
         self.deleted_data = []
         # Process change forms
         for form in self.change_forms:
-            if form.is_valid():
+            if form.is_bound:
+                form.full_clean(keep_invalid=True)
+            if hasattr(form, 'cleaned_data'):
                 if self.deletable and form.cleaned_data[DELETION_FIELD_NAME]:
                     self.deleted_data.append(form.cleaned_data)
+                elif form.is_valid():
+                    self.cleaned_data.append(form.cleaned_data)
                 else:
-                    self.cleaned_data.append(form.cleaned_data)
+                    self._is_valid = False
+                    errors.append(form.errors)
             else:
-                self._is_valid = False
-            errors.append(form.errors)
+                self._is_valid = False        
+                errors.append(form.errors)
         # Process add forms in reverse so we can easily tell when the remaining
         # ones should be required.
         reamining_forms_required = False
@@ -167,7 +172,7 @@
         via formset.non_form_errors()
         """
         return self.cleaned_data
-
+                
     def add_fields(self, form, index):
         """A hook for adding extra fields on to each form instance."""
         if self.orderable:
Index: django/newforms/models.py
===================================================================
--- django/newforms/models.py	(revision 6777)
+++ django/newforms/models.py	(working copy)
@@ -313,7 +313,7 @@
             obj = existing_objects[form.cleaned_data[self.model._meta.pk.attname]]
             if self.deletable and form.cleaned_data[DELETION_FIELD_NAME]:
                 obj.delete()
-            else:
+            elif self.is_valid():
                 saved_instances.append(self.save_instance(form, obj, commit=commit))
         return saved_instances
 
@@ -327,7 +327,8 @@
             # the deletion widget for add forms.
             if self.deletable and form.cleaned_data[DELETION_FIELD_NAME]:
                 continue
-            new_objects.append(self.save_new(form, commit=commit))
+            if self.is_valid():
+                new_objects.append(self.save_new(form, commit=commit))
         return new_objects
 
     def add_fields(self, form, index):
Index: django/newforms/forms.py
===================================================================
--- django/newforms/forms.py	(revision 6777)
+++ django/newforms/forms.py	(working copy)
@@ -191,7 +191,7 @@
                 return False
         return True
 
-    def full_clean(self):
+    def full_clean(self, keep_invalid=False):
         """
         Cleans all of self.data and populates self._errors and
         self.cleaned_data.
@@ -219,7 +219,7 @@
             self.cleaned_data = self.clean()
         except ValidationError, e:
             self._errors[NON_FIELD_ERRORS] = e.messages
-        if self._errors:
+        if not keep_invalid and self._errors:
             delattr(self, 'cleaned_data')
 
     def clean(self):
Index: tests/regressiontests/forms/widgets.py
===================================================================
--- tests/regressiontests/forms/widgets.py	(revision 6777)
+++ tests/regressiontests/forms/widgets.py	(working copy)
@@ -292,6 +292,12 @@
 >>> w.value_from_datadict({}, {}, 'testing')
 False
 
+The CheckboxInput widget will always be empty when there is a False value
+>>> w.is_empty(False)
+True
+>>> w.is_empty(True)
+False
+
 # Select Widget ###############################################################
 
 >>> w = Select()
@@ -453,6 +459,15 @@
 <option value="3" selected="selected">No</option>
 </select>
 
+The NullBooleanSelect widget will always be empty when Unknown or No is selected
+as its value.  This is to stay compliant with the CheckboxInput behavior
+>>> w.is_empty(False)
+True
+>>> w.is_empty(None)
+True
+>>> w.is_empty(True)
+False
+
 """ + \
 r""" # [This concatenation is to keep the string below the jython's 32K limit].
 # SelectMultiple Widget #######################################################
@@ -895,6 +910,16 @@
 >>> w.render('name', ['john', 'lennon'])
 u'<input id="bar_0" type="text" class="big" value="john" name="name_0" /><br /><input id="bar_1" type="text" class="small" value="lennon" name="name_1" />'
 
+The MultiWidget will be empty only when all widgets are considered empty.
+>>> w.is_empty(['john', 'lennon'])
+False
+>>> w.is_empty(['john', ''])
+False
+>>> w.is_empty(['', ''])
+True
+>>> w.is_empty([None, None])
+True
+
 # SplitDateTimeWidget #########################################################
 
 >>> w = SplitDateTimeWidget()
