1 from django import forms
2 from django.contrib.admin.widgets import AdminTextareaWidget
3 from django.core.exceptions import ObjectDoesNotExist
4 from django.db.models import Q
5 from django.forms.models import ModelForm, BaseInlineFormSet, BaseModelFormSet
6 from django.forms.formsets import TOTAL_FORM_COUNT
7 from django.utils.datastructures import SortedDict
9 from philo.admin.widgets import ModelLookupWidget
10 from philo.models import Contentlet, ContentReference
15 'ContentletInlineFormSet',
16 'ContentReferenceForm',
17 'ContentReferenceInlineFormSet'
21 class ContainerForm(ModelForm):
22 def __init__(self, *args, **kwargs):
23 super(ContainerForm, self).__init__(*args, **kwargs)
24 self.verbose_name = self.instance.name.replace('_', ' ')
25 self.prefix = self.instance.name
28 class ContentletForm(ContainerForm):
29 content = forms.CharField(required=False, widget=AdminTextareaWidget, label='Content')
31 def should_delete(self):
32 # Delete iff: the data has changed and is now empty.
33 return self.has_changed() and not bool(self.cleaned_data['content'])
40 class ContentReferenceForm(ContainerForm):
41 def __init__(self, *args, **kwargs):
42 super(ContentReferenceForm, self).__init__(*args, **kwargs)
44 self.fields['content_id'].widget = ModelLookupWidget(self.instance.content_type)
45 except ObjectDoesNotExist:
46 # This will happen when an empty form (which we will never use) gets instantiated.
49 def should_delete(self):
50 return self.has_changed() and (self.cleaned_data['content_id'] is None)
53 model = ContentReference
54 fields = ['content_id']
57 class ContainerInlineFormSet(BaseInlineFormSet):
60 if not hasattr(self, '_containers'):
61 self._containers = self.get_containers()
62 return self._containers
64 def total_form_count(self):
65 # This ignores the posted management form data... but that doesn't
66 # seem to have any ill side effects.
67 return len(self.containers.keys())
69 def _get_initial_forms(self):
70 return [form for form in self.forms if form.instance.pk is not None]
71 initial_forms = property(_get_initial_forms)
73 def _get_extra_forms(self):
74 return [form for form in self.forms if form.instance.pk is None]
75 extra_forms = property(_get_extra_forms)
77 def _construct_form(self, i, **kwargs):
78 if 'instance' not in kwargs:
79 kwargs['instance'] = self.containers.values()[i]
81 # Skip over the BaseModelFormSet. We have our own way of doing things!
82 form = super(BaseModelFormSet, self)._construct_form(i, **kwargs)
84 # Since we skipped over BaseModelFormSet, we need to duplicate what BaseInlineFormSet would do.
86 # Remove the primary key from the form's data, we are only
87 # creating new instances
88 form.data[form.add_prefix(self._pk_field.name)] = None
90 # Remove the foreign key from the form's data
91 form.data[form.add_prefix(self.fk.name)] = None
93 # Set the fk value here so that the form can do it's validation.
94 setattr(form.instance, self.fk.get_attname(), self.instance.pk)
97 def add_fields(self, form, index):
98 """Override the pk field's initial value with a real one."""
99 super(ContainerInlineFormSet, self).add_fields(form, index)
100 if index is not None:
101 pk_value = self.containers.values()[index].pk
104 form.fields[self._pk_field.name].initial = pk_value
106 def save_existing_objects(self, commit=True):
107 self.changed_objects = []
108 self.deleted_objects = []
109 if not self.get_queryset():
113 for form in self.initial_forms:
114 pk_name = self._pk_field.name
115 raw_pk_value = form._raw_value(pk_name)
117 # clean() for different types of PK fields can sometimes return
118 # the model instance, and sometimes the PK. Handle either.
119 pk_value = form.fields[pk_name].clean(raw_pk_value)
120 pk_value = getattr(pk_value, 'pk', pk_value)
122 # if the pk_value is None, they have just switched to a
123 # template which didn't contain data about this container.
125 if pk_value is not None:
126 obj = self._existing_object(pk_value)
127 if form.should_delete():
128 self.deleted_objects.append(obj)
131 if form.has_changed():
132 self.changed_objects.append((obj, form.changed_data))
133 saved_instances.append(self.save_existing(form, obj, commit=commit))
135 self.saved_forms.append(form)
136 return saved_instances
138 def save_new_objects(self, commit=True):
139 self.new_objects = []
140 for form in self.extra_forms:
141 if not form.has_changed():
143 # If someone has marked an add form for deletion, don't save the
145 if form.should_delete():
147 self.new_objects.append(self.save_new(form, commit=commit))
149 self.saved_forms.append(form)
150 return self.new_objects
153 class ContentletInlineFormSet(ContainerInlineFormSet):
154 def get_containers(self):
156 containers = list(self.instance.containers[0])
157 except ObjectDoesNotExist:
160 qs = self.get_queryset().filter(name__in=containers)
161 container_dict = SortedDict([(container.name, container) for container in qs])
162 for name in containers:
163 if name not in container_dict:
164 container_dict[name] = self.model(name=name)
166 container_dict.keyOrder = containers
167 return container_dict
170 class ContentReferenceInlineFormSet(ContainerInlineFormSet):
171 def get_containers(self):
173 containers = self.instance.containers[1]
174 except ObjectDoesNotExist:
178 for name, ct in containers.items():
179 filter |= Q(name=name, content_type=ct)
180 qs = self.get_queryset().filter(filter)
182 container_dict = SortedDict([(container.name, container) for container in qs])
185 for name, ct in containers.items():
186 keyOrder.append(name)
187 if name not in container_dict:
188 container_dict[name] = self.model(name=name, content_type=ct)
190 container_dict.keyOrder = keyOrder
191 return container_dict