diff --git a/django/forms/forms.py b/django/forms/forms.py index e28479fb8b..04d4e6634c 100644 --- a/django/forms/forms.py +++ b/django/forms/forms.py @@ -205,6 +205,15 @@ class BaseForm(StrAndUnicode): """ return self.errors.get(NON_FIELD_ERRORS, self.error_class()) + def _raw_value(self, fieldname): + """ + Returns the raw_value for a particular field name. This is just a + convenient wrapper around widget.value_from_datadict. + """ + field = self.fields[fieldname] + prefix = self.add_prefix(fieldname) + return field.widget.value_from_datadict(self.data, self.files, prefix) + def full_clean(self): """ Cleans all of self.data and populates self._errors and diff --git a/django/forms/formsets.py b/django/forms/formsets.py index 81353070a9..a7e27713dc 100644 --- a/django/forms/formsets.py +++ b/django/forms/formsets.py @@ -216,9 +216,8 @@ class BaseFormSet(StrAndUnicode): # more code than we'd like, but the form's cleaned_data will # not exist if the form is invalid. field = form.fields[DELETION_FIELD_NAME] - prefix = form.add_prefix(DELETION_FIELD_NAME) - value = field.widget.value_from_datadict(self.data, self.files, prefix) - should_delete = field.clean(value) + raw_value = form._raw_value(DELETION_FIELD_NAME) + should_delete = field.clean(raw_value) if should_delete: # This form is going to be deleted so any of its errors # should not cause the entire formset to be invalid. diff --git a/django/forms/models.py b/django/forms/models.py index bfc0b35803..a3c8f355a4 100644 --- a/django/forms/models.py +++ b/django/forms/models.py @@ -408,16 +408,22 @@ class BaseModelFormSet(BaseFormSet): existing_objects[obj.pk] = obj saved_instances = [] for form in self.initial_forms: - obj = existing_objects[form.cleaned_data[self._pk_field.name]] - if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]: - self.deleted_objects.append(obj) - obj.delete() - else: - if form.changed_data: - self.changed_objects.append((obj, form.changed_data)) - saved_instances.append(self.save_existing(form, obj, commit=commit)) - if not commit: - self.saved_forms.append(form) + pk_name = self._pk_field.name + raw_pk_value = form._raw_value(pk_name) + pk_value = form.fields[pk_name].clean(raw_pk_value) + obj = existing_objects[pk_value] + if self.can_delete: + raw_delete_value = form._raw_value(DELETION_FIELD_NAME) + should_delete = form.fields[DELETION_FIELD_NAME].clean(raw_delete_value) + if should_delete: + self.deleted_objects.append(obj) + obj.delete() + continue + if form.changed_data: + self.changed_objects.append((obj, form.changed_data)) + saved_instances.append(self.save_existing(form, obj, commit=commit)) + if not commit: + self.saved_forms.append(form) return saved_instances def save_new_objects(self, commit=True): @@ -427,8 +433,11 @@ class BaseModelFormSet(BaseFormSet): continue # If someone has marked an add form for deletion, don't save the # object. - if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]: - continue + if self.can_delete: + raw_delete_value = form._raw_value(DELETION_FIELD_NAME) + should_delete = form.fields[DELETION_FIELD_NAME].clean(raw_delete_value) + if should_delete: + continue self.new_objects.append(self.save_new(form, commit=commit)) if not commit: self.saved_forms.append(form) diff --git a/tests/modeltests/model_formsets/models.py b/tests/modeltests/model_formsets/models.py index 0503ea2a84..a65291a698 100644 --- a/tests/modeltests/model_formsets/models.py +++ b/tests/modeltests/model_formsets/models.py @@ -150,6 +150,19 @@ class Player(models.Model): def __unicode__(self): return self.name +class Poet(models.Model): + name = models.CharField(max_length=100) + + def __unicode__(self): + return self.name + +class Poem(models.Model): + poet = models.ForeignKey(Poet) + name = models.CharField(max_length=100) + + def __unicode__(self): + return self.name + __test__ = {'API_TESTS': """ >>> from datetime import date diff --git a/tests/regressiontests/inline_formsets/models.py b/tests/regressiontests/inline_formsets/models.py index 8b6f5dd342..9b1f8b4932 100644 --- a/tests/regressiontests/inline_formsets/models.py +++ b/tests/regressiontests/inline_formsets/models.py @@ -13,6 +13,19 @@ class Child(models.Model): school = models.ForeignKey(School) name = models.CharField(max_length=100) +class Poet(models.Model): + name = models.CharField(max_length=100) + + def __unicode__(self): + return self.name + +class Poem(models.Model): + poet = models.ForeignKey(Poet) + name = models.CharField(max_length=100) + + def __unicode__(self): + return self.name + __test__ = {'API_TESTS': """ >>> from django.forms.models import inlineformset_factory