1 from django.contrib.contenttypes.generic import BaseGenericInlineFormSet
2 from django.contrib.contenttypes.models import ContentType
3 from django.forms.models import ModelForm
4 from philo.models import Attribute
7 __all__ = ('AttributeForm', 'AttributeInlineFormSet')
10 class AttributeForm(ModelForm):
12 This class handles an attribute's fields as well as the fields for its value (if there is one.)
13 The fields defined will vary depending on the value type, but the fields for defining the value
14 (i.e. value_content_type and value_object_id) will always be defined. Except that value_object_id
15 will never be defined. BLARGH!
17 def __init__(self, *args, **kwargs):
18 super(AttributeForm, self).__init__(*args, **kwargs)
20 # This is necessary because model forms store changes to self.instance in their clean method.
22 value = self.instance.value
23 self._cached_value_ct = self.instance.value_content_type
24 self._cached_value = value
26 # If there is a value, pull in its fields.
28 self.value_fields = value.value_formfields()
29 self.fields.update(self.value_fields)
31 def save(self, *args, **kwargs):
32 # At this point, the cleaned_data has already been stored on self.instance.
34 if self.instance.value_content_type != self._cached_value_ct:
35 # The value content type has changed. Clear the old value, if there was one.
36 if self._cached_value:
37 self._cached_value.delete()
39 # Clear the submitted value, if any.
40 self.cleaned_data.pop('value', None)
42 # Now create a new value instance so that on next instantiation, the form will
43 # know what fields to add.
44 if self.instance.value_content_type is not None:
45 self.instance.value = self.instance.value_content_type.model_class().objects.create()
46 elif self.instance.value is not None:
47 # The value content type is the same, but one of the value fields has changed.
49 # Use construct_instance to apply the changes from the cleaned_data to the value instance.
50 fields = self.value_fields.keys()
51 if set(fields) & set(self.changed_data):
52 self.instance.value.construct_instance(**dict([(key, self.cleaned_data[key]) for key in fields]))
53 self.instance.value.save()
55 return super(AttributeForm, self).save(*args, **kwargs)
61 class AttributeInlineFormSet(BaseGenericInlineFormSet):
62 "Necessary to force the GenericInlineFormset to use the form's save method for new objects."
63 def save_new(self, form, commit):
64 setattr(form.instance, self.ct_field.get_attname(), ContentType.objects.get_for_model(self.instance).pk)
65 setattr(form.instance, self.ct_fk_field.get_attname(), self.instance.pk)