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
8 from philo.admin.widgets import ModelLookupWidget
9 from philo.models import Contentlet, ContentReference
14 'ContentletInlineFormSet',
15 'ContentReferenceForm',
16 'ContentReferenceInlineFormSet'
20 class ContainerForm(ModelForm):
21 def __init__(self, *args, **kwargs):
22 super(ContainerForm, self).__init__(*args, **kwargs)
23 self.verbose_name = self.instance.name.replace('_', ' ')
24 self.prefix = self.instance.name
27 class ContentletForm(ContainerForm):
28 content = forms.CharField(required=False, widget=AdminTextareaWidget, label='Content')
30 def should_delete(self):
31 # Delete iff: the data has changed and is now empty.
32 return self.has_changed() and not bool(self.cleaned_data['content'])
39 class ContentReferenceForm(ContainerForm):
40 def __init__(self, *args, **kwargs):
41 super(ContentReferenceForm, self).__init__(*args, **kwargs)
43 self.fields['content_id'].widget = ModelLookupWidget(self.instance.content_type)
44 except ObjectDoesNotExist:
45 # This will happen when an empty form (which we will never use) gets instantiated.
48 def should_delete(self):
49 return self.has_changed() and (self.cleaned_data['content_id'] is None)
52 model = ContentReference
53 fields = ['content_id']
56 class ContainerInlineFormSet(BaseInlineFormSet):
59 if not hasattr(self, '_containers'):
60 self._containers = self.get_containers()
61 return self._containers
63 def total_form_count(self):
64 # This ignores the posted management form data... but that doesn't
65 # seem to have any ill side effects.
66 return len(self.containers.keys())
68 def _get_initial_forms(self):
69 return [form for form in self.forms if form.instance.pk is not None]
70 initial_forms = property(_get_initial_forms)
72 def _get_extra_forms(self):
73 return [form for form in self.forms if form.instance.pk is None]
74 extra_forms = property(_get_extra_forms)
76 def _construct_form(self, i, **kwargs):
77 if 'instance' not in kwargs:
78 kwargs['instance'] = self.containers.values()[i]
80 # Skip over the BaseModelFormSet. We have our own way of doing things!
81 form = super(BaseModelFormSet, self)._construct_form(i, **kwargs)
83 # Since we skipped over BaseModelFormSet, we need to duplicate what BaseInlineFormSet would do.
85 # Remove the primary key from the form's data, we are only
86 # creating new instances
87 form.data[form.add_prefix(self._pk_field.name)] = None
89 # Remove the foreign key from the form's data
90 form.data[form.add_prefix(self.fk.name)] = None
92 # Set the fk value here so that the form can do it's validation.
93 setattr(form.instance, self.fk.get_attname(), self.instance.pk)
96 def add_fields(self, form, index):
97 """Override the pk field's initial value with a real one."""
98 super(ContainerInlineFormSet, self).add_fields(form, index)
100 pk_value = self.containers.values()[index].pk
103 form.fields[self._pk_field.name].initial = pk_value
105 def save_existing_objects(self, commit=True):
106 self.changed_objects = []
107 self.deleted_objects = []
108 if not self.get_queryset():
112 for form in self.initial_forms:
113 pk_name = self._pk_field.name
114 raw_pk_value = form._raw_value(pk_name)
116 # clean() for different types of PK fields can sometimes return
117 # the model instance, and sometimes the PK. Handle either.
118 pk_value = form.fields[pk_name].clean(raw_pk_value)
119 pk_value = getattr(pk_value, 'pk', pk_value)
121 # if the pk_value is None, they have just switched to a
122 # template which didn't contain data about this container.
124 if pk_value is not None:
125 obj = self._existing_object(pk_value)
126 if form.should_delete():
127 self.deleted_objects.append(obj)
130 if form.has_changed():
131 self.changed_objects.append((obj, form.changed_data))
132 saved_instances.append(self.save_existing(form, obj, commit=commit))
134 self.saved_forms.append(form)
135 return saved_instances
137 def save_new_objects(self, commit=True):
138 self.new_objects = []
139 for form in self.extra_forms:
140 if not form.has_changed():
142 # If someone has marked an add form for deletion, don't save the
144 if form.should_delete():
146 self.new_objects.append(self.save_new(form, commit=commit))
148 self.saved_forms.append(form)
149 return self.new_objects
152 class ContentletInlineFormSet(ContainerInlineFormSet):
153 def get_containers(self):
155 containers = list(self.instance.containers[0])
156 except ObjectDoesNotExist:
159 qs = self.get_queryset().filter(name__in=containers)
160 container_dict = SortedDict([(container.name, container) for container in qs])
161 for name in containers:
162 if name not in container_dict:
163 container_dict[name] = self.model(name=name)
165 container_dict.keyOrder = containers
166 return container_dict
169 class ContentReferenceInlineFormSet(ContainerInlineFormSet):
170 def get_containers(self):
172 containers = self.instance.containers[1]
173 except ObjectDoesNotExist:
177 for name, ct in containers.items():
178 filter |= Q(name=name, content_type=ct)
179 qs = self.get_queryset().filter(filter)
181 container_dict = SortedDict([(container.name, container) for container in qs])
184 for name, ct in containers.items():
185 keyOrder.append(name)
186 if name not in container_dict:
187 container_dict[name] = self.model(name=name, content_type=ct)
189 container_dict.keyOrder = keyOrder
190 return container_dict