Opened 6 years ago

Last modified 6 years ago

#14009 new Bug

custom formset validation documentation is incomplete

Reported by: splatEric <mike@…> Owned by: nobody
Component: Documentation Version: 1.2
Severity: Normal Keywords: formset validation
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no


The example for performing custom formset validation is incomplete. It does not take into account deleted forms, or empty forms for checking the unique titles constraint. It should read something along the lines of (changes highlighted by *>):

>>> class BaseArticleFormSet(BaseFormSet):
...     def clean(self):
...         """Checks that no two articles have the same title."""
...         if any(self.errors):
...             # Don't bother validating the formset unless each form is valid on its own
...             return
...         titles = []
...         for i in range(0, self.total_form_count()):
*>             if self.can_delete and self._should_delete_form(form):
*>                continue
...             form = self.forms[i]
*>             if 'title' in form.cleaned_data:
...                 title = form.cleaned_data['title']
...                 if title in titles:
...                     raise forms.ValidationError, "Articles in a set must have distinct titles."
...                 titles.append(title)

Change History (5)

comment:1 Changed 6 years ago by Gabriel Hurley

Has patch: unset
Needs documentation: unset
Needs tests: unset
Patch needs improvement: unset
Triage Stage: UnreviewedAccepted

Accepted. For future reference, the file in question is docs/topics/forms/formsets/.

comment:2 Changed 6 years ago by leonelfreire

Severity: Normal
Type: Uncategorized


I think there's an "error" with the above suggestion too. self.errors will contain the errors of all forms (even if the form is marked for deletion). Supposing all NOT DELETED forms are valid, if some deleted form is in an invalid state, the code:

if any(self.errors):

will make the "clean" method return too soon, avoiding the execution of the validation of the NOT DELETED forms.

AFAIK, formset.is_valid() can be True, even if formset.errors contains forms errors (from deleted forms).

In my case, I did something like:

def clean(self)
	if self.is_valid():
			# Get only the forms not marked for deletion
			cleaned_forms = list(set(self.forms) - set(self.deleted_forms))
		except AttributError: # There're no forms marked for deletion (no self.deleted_forms)
			cleaned_forms = self.forms
		# validate logic with cleaned_forms

If there's a better way to do this, let me know.

comment:3 Changed 5 years ago by Julien Phalip

Type: UncategorizedBug

comment:4 Changed 5 years ago by Aymeric Augustin

UI/UX: unset

Change UI/UX from NULL to False.

comment:5 Changed 5 years ago by Aymeric Augustin

Easy pickings: unset

Change Easy pickings from NULL to False.

Note: See TracTickets for help on using tickets.
Back to Top