X-Git-Url: http://git.ithinksw.org/philo.git/blobdiff_plain/82b08f79564159d7acbcaf255ed1ac1fb4882e64..d19e216035b14d8f60b24dda0c0670e6997f16ce:/philo/admin/forms/attributes.py diff --git a/philo/admin/forms/attributes.py b/philo/admin/forms/attributes.py new file mode 100644 index 0000000..fc77d0f --- /dev/null +++ b/philo/admin/forms/attributes.py @@ -0,0 +1,66 @@ +from django.contrib.contenttypes.generic import BaseGenericInlineFormSet +from django.contrib.contenttypes.models import ContentType +from django.forms.models import ModelForm +from philo.models import Attribute + + +__all__ = ('AttributeForm', 'AttributeInlineFormSet') + + +class AttributeForm(ModelForm): + """ + This class handles an attribute's fields as well as the fields for its value (if there is one.) + The fields defined will vary depending on the value type, but the fields for defining the value + (i.e. value_content_type and value_object_id) will always be defined. Except that value_object_id + will never be defined. BLARGH! + """ + def __init__(self, *args, **kwargs): + super(AttributeForm, self).__init__(*args, **kwargs) + + # This is necessary because model forms store changes to self.instance in their clean method. + # Mutter mutter. + value = self.instance.value + self._cached_value_ct = self.instance.value_content_type + self._cached_value = value + + # If there is a value, pull in its fields. + if value is not None: + self.value_fields = value.value_formfields() + self.fields.update(self.value_fields) + + def save(self, *args, **kwargs): + # At this point, the cleaned_data has already been stored on self.instance. + + if self.instance.value_content_type != self._cached_value_ct: + # The value content type has changed. Clear the old value, if there was one. + if self._cached_value: + self._cached_value.delete() + + # Clear the submitted value, if any. + self.cleaned_data.pop('value', None) + + # Now create a new value instance so that on next instantiation, the form will + # know what fields to add. + if self.instance.value_content_type is not None: + self.instance.value = self.instance.value_content_type.model_class().objects.create() + elif self.instance.value is not None: + # The value content type is the same, but one of the value fields has changed. + + # Use construct_instance to apply the changes from the cleaned_data to the value instance. + fields = self.value_fields.keys() + if set(fields) & set(self.changed_data): + self.instance.value.construct_instance(**dict([(key, self.cleaned_data[key]) for key in fields])) + self.instance.value.save() + + return super(AttributeForm, self).save(*args, **kwargs) + + class Meta: + model = Attribute + + +class AttributeInlineFormSet(BaseGenericInlineFormSet): + "Necessary to force the GenericInlineFormset to use the form's save method for new objects." + def save_new(self, form, commit): + setattr(form.instance, self.ct_field.get_attname(), ContentType.objects.get_for_model(self.instance).pk) + setattr(form.instance, self.ct_fk_field.get_attname(), self.instance.pk) + return form.save() \ No newline at end of file