Skip to content

Instantly share code, notes, and snippets.

@akaihola
Created February 17, 2010 15:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save akaihola/306693 to your computer and use it in GitHub Desktop.
Save akaihola/306693 to your computer and use it in GitHub Desktop.
"""
An inline formset which deletes objects when all visible fields are empty or
all-whitespace instead of using the delete checkbox.
See
http://groups.google.com/group/django-users/browse_thread/thread/23539f5e085e62b0
for discussion.
"""
from django.forms import HiddenInput, ValidationError
from django.forms.util import ErrorDict
from django.forms.models import ModelForm, BaseInlineFormSet
from django.forms.formsets import DELETION_FIELD_NAME
class DeleteIfEmptyModelForm(ModelForm):
"""
A ModelForm which marks itself valid and empty if all visible fields are
blank or all-whitespace.
"""
def full_clean(self):
if not self.is_bound:
return
if not self.is_empty():
return super(DeleteIfEmptyModelForm, self).full_clean()
self._errors = ErrorDict()
def is_empty(self):
if not hasattr(self, 'cleaned_data'):
self.cleaned_data = {}
if not hasattr(self, '_empty'):
self._empty = True
for boundfield in self:
field = self.fields[boundfield.name]
value = field.widget.value_from_datadict(
self.data, self.files, self.add_prefix(boundfield.name))
if not boundfield.is_hidden and boundfield.name != DELETION_FIELD_NAME and value is not None and unicode(value).strip():
self._empty = False
break
try:
clean_value = field.clean(value)
except ValidationError:
clean_value = ''
self.cleaned_data[boundfield.name] = clean_value
return self._empty
class DeleteIfEmptyInlineFormSet(BaseInlineFormSet):
"""
Modified version of django.forms.models.BaseInlineFormSet for allowing
deleting objects by emptying their visible fields instead of checking the
delete checkbox.
Not overriding _get_deleted_forms() since it doesn't seem to be used in my
use case.
"""
def save_existing_objects(self, commit=True):
self.changed_objects = []
self.deleted_objects = []
if not self.get_queryset():
return []
# Put the objects from self.get_queryset into a dict so they are easy to lookup by pk
existing_objects = {}
for obj in self.get_queryset():
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] or form.is_empty()):
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)
return saved_instances
def add_fields(self, form, index):
"""Override delete field and make it hidden."""
super(DeleteIfEmptyInlineFormSet, self).add_fields(form, index)
form.fields[DELETION_FIELD_NAME].widget = HiddenInput()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment