-
Notifications
You must be signed in to change notification settings - Fork 36
/
Copy pathgenericextensions.py
114 lines (97 loc) · 4.43 KB
/
genericextensions.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
from django.contrib.contenttypes.generic import BaseGenericInlineFormSet
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from django.forms.formsets import ManagementForm
class BaseInitialGenericInlineFormSet(BaseGenericInlineFormSet):
"""
A formset that can take initial values, and pass those to the generated forms.
"""
# Based on http://stackoverflow.com/questions/442040/pre-populate-an-inline-formset/3766344#3766344
def __init__(self, *args, **kwargs):
"""
Grabs the curried initial values and stores them into a 'private' variable.
"""
# This instance is created each time in the change_view() function.
self._initial = kwargs.pop('initial', [])
super(BaseInitialGenericInlineFormSet, self).__init__(*args, **kwargs)
@property
def management_form(self):
try:
return super(BaseInitialGenericInlineFormSet, self).management_form
except ValidationError:
# Provide with better description of what is happening.
form = ManagementForm(self.data, auto_id=self.auto_id, prefix=self.prefix)
if not form.is_valid():
raise ValidationError(
u'ManagementForm data is missing or has been tampered with.'
u' form: {0}, model: {1}, errors: \n{2}'.format(
self.__class__.__name__, self.model.__name__,
form.errors.as_text()
))
else:
raise
def initial_form_count(self):
if self.is_bound:
return super(BaseInitialGenericInlineFormSet, self).initial_form_count()
else:
return len(self.get_queryset())
def total_form_count(self):
if self.is_bound:
return super(BaseInitialGenericInlineFormSet, self).total_form_count()
else:
return max(len(self.get_queryset()), len(self._initial)) + self.extra
def _construct_form(self, i, **kwargs):
if self._initial and not kwargs.get('instance', None):
instance = self.__get_form_instance(i)
if instance:
kwargs['instance'] = instance
form = super(BaseInitialGenericInlineFormSet, self)._construct_form(i, **kwargs)
return form
def __initial_minus_queryset(self):
"""
Gives all elements from self._initial having a slot value that is not already
in self.get_queryset()
"""
queryset = self.get_queryset()
def initial_not_in_queryset(initial):
for x in queryset:
if x.slot == initial['slot']:
return False
return True
return list(filter(initial_not_in_queryset, self._initial))
def __queryset_minus_initial(self):
"""
Gives all objects from ``self.get_queryset()`` having a slot value that
is not already in ``self._initial``.
"""
def queryset_not_in_initial(instance):
for values in self._initial:
if values['slot'] == instance.slot:
return False
return True
return list(filter(queryset_not_in_initial, self.get_queryset()))
def __get_form_instance(self, i):
# Get slot name from initial values, or from queryset objects without
# initial data (by index), when placeholders have been removed from a
# template but still exist in the database.
try:
slot = self._initial[i]['slot']
except IndexError:
# Getting by index is just a guess. Is there a better way? Content
# items belonging to these placeholders should be orphaned?
slot = self.__queryset_minus_initial()[i - len(self._initial)].slot
# Editing existing object. Make sure the ID is passed.
for instance in self.get_queryset():
if instance.slot == slot:
return instance
# Adding new object, pass initial values
for values in self.__initial_minus_queryset():
if values['slot'] == slot:
break
else:
raise KeyError('No slot named %r. This should never happen.' % slot)
values[self.ct_field.name] = ContentType.objects.get_for_model(self.instance)
values[self.ct_fk_field.name] = self.instance.pk
instance = self.model(**values)
# instance.save()
return instance