root / env / lib / python2.7 / site-packages / django / contrib / admin / helpers.py @ 1a305335
History | View | Annotate | Download (13.3 KB)
1 | 1a305335 | officers | from django import forms |
---|---|---|---|
2 | from django.contrib.admin.util import (flatten_fieldsets, lookup_field, |
||
3 | display_for_field, label_for_field, help_text_for_field) |
||
4 | from django.contrib.admin.templatetags.admin_static import static |
||
5 | from django.contrib.contenttypes.models import ContentType |
||
6 | from django.core.exceptions import ObjectDoesNotExist |
||
7 | from django.db.models.fields.related import ManyToManyRel |
||
8 | from django.forms.util import flatatt |
||
9 | from django.template.defaultfilters import capfirst |
||
10 | from django.utils.encoding import force_unicode, smart_unicode |
||
11 | from django.utils.html import escape, conditional_escape |
||
12 | from django.utils.safestring import mark_safe |
||
13 | from django.utils.translation import ugettext_lazy as _ |
||
14 | from django.conf import settings |
||
15 | |||
16 | |||
17 | ACTION_CHECKBOX_NAME = '_selected_action'
|
||
18 | |||
19 | class ActionForm(forms.Form): |
||
20 | action = forms.ChoiceField(label=_('Action:'))
|
||
21 | select_across = forms.BooleanField(label='', required=False, initial=0, |
||
22 | widget=forms.HiddenInput({'class': 'select-across'})) |
||
23 | |||
24 | checkbox = forms.CheckboxInput({'class': 'action-select'}, lambda value: False) |
||
25 | |||
26 | class AdminForm(object): |
||
27 | def __init__(self, form, fieldsets, prepopulated_fields, readonly_fields=None, model_admin=None): |
||
28 | self.form, self.fieldsets = form, normalize_fieldsets(fieldsets) |
||
29 | self.prepopulated_fields = [{
|
||
30 | 'field': form[field_name],
|
||
31 | 'dependencies': [form[f] for f in dependencies] |
||
32 | } for field_name, dependencies in prepopulated_fields.items()] |
||
33 | self.model_admin = model_admin
|
||
34 | if readonly_fields is None: |
||
35 | readonly_fields = () |
||
36 | self.readonly_fields = readonly_fields
|
||
37 | |||
38 | def __iter__(self): |
||
39 | for name, options in self.fieldsets: |
||
40 | yield Fieldset(self.form, name, |
||
41 | readonly_fields=self.readonly_fields,
|
||
42 | model_admin=self.model_admin,
|
||
43 | **options |
||
44 | ) |
||
45 | |||
46 | def first_field(self): |
||
47 | try:
|
||
48 | fieldset_name, fieldset_options = self.fieldsets[0] |
||
49 | field_name = fieldset_options['fields'][0] |
||
50 | if not isinstance(field_name, basestring): |
||
51 | field_name = field_name[0]
|
||
52 | return self.form[field_name] |
||
53 | except (KeyError, IndexError): |
||
54 | pass
|
||
55 | try:
|
||
56 | return iter(self.form).next() |
||
57 | except StopIteration: |
||
58 | return None |
||
59 | |||
60 | def _media(self): |
||
61 | media = self.form.media
|
||
62 | for fs in self: |
||
63 | media = media + fs.media |
||
64 | return media
|
||
65 | media = property(_media)
|
||
66 | |||
67 | class Fieldset(object): |
||
68 | def __init__(self, form, name=None, readonly_fields=(), fields=(), classes=(), |
||
69 | description=None, model_admin=None): |
||
70 | self.form = form
|
||
71 | self.name, self.fields = name, fields |
||
72 | self.classes = u' '.join(classes) |
||
73 | self.description = description
|
||
74 | self.model_admin = model_admin
|
||
75 | self.readonly_fields = readonly_fields
|
||
76 | |||
77 | def _media(self): |
||
78 | if 'collapse' in self.classes: |
||
79 | extra = '' if settings.DEBUG else '.min' |
||
80 | js = ['jquery%s.js' % extra,
|
||
81 | 'jquery.init.js',
|
||
82 | 'collapse%s.js' % extra]
|
||
83 | return forms.Media(js=[static('admin/js/%s' % url) for url in js]) |
||
84 | return forms.Media()
|
||
85 | media = property(_media)
|
||
86 | |||
87 | def __iter__(self): |
||
88 | for field in self.fields: |
||
89 | yield Fieldline(self.form, field, self.readonly_fields, model_admin=self.model_admin) |
||
90 | |||
91 | class Fieldline(object): |
||
92 | def __init__(self, form, field, readonly_fields=None, model_admin=None): |
||
93 | self.form = form # A django.forms.Form instance |
||
94 | if not hasattr(field, "__iter__"): |
||
95 | self.fields = [field]
|
||
96 | else:
|
||
97 | self.fields = field
|
||
98 | self.model_admin = model_admin
|
||
99 | if readonly_fields is None: |
||
100 | readonly_fields = () |
||
101 | self.readonly_fields = readonly_fields
|
||
102 | |||
103 | def __iter__(self): |
||
104 | for i, field in enumerate(self.fields): |
||
105 | if field in self.readonly_fields: |
||
106 | yield AdminReadonlyField(self.form, field, is_first=(i == 0), |
||
107 | model_admin=self.model_admin)
|
||
108 | else:
|
||
109 | yield AdminField(self.form, field, is_first=(i == 0)) |
||
110 | |||
111 | def errors(self): |
||
112 | return mark_safe(u'\n'.join([self.form[f].errors.as_ul() for f in self.fields if f not in self.readonly_fields]).strip('\n')) |
||
113 | |||
114 | class AdminField(object): |
||
115 | def __init__(self, form, field, is_first): |
||
116 | self.field = form[field] # A django.forms.BoundField instance |
||
117 | self.is_first = is_first # Whether this field is first on the line |
||
118 | self.is_checkbox = isinstance(self.field.field.widget, forms.CheckboxInput) |
||
119 | |||
120 | def label_tag(self): |
||
121 | classes = [] |
||
122 | contents = conditional_escape(force_unicode(self.field.label))
|
||
123 | if self.is_checkbox: |
||
124 | classes.append(u'vCheckboxLabel')
|
||
125 | else:
|
||
126 | contents += u':'
|
||
127 | if self.field.field.required: |
||
128 | classes.append(u'required')
|
||
129 | if not self.is_first: |
||
130 | classes.append(u'inline')
|
||
131 | attrs = classes and {'class': u' '.join(classes)} or {} |
||
132 | return self.field.label_tag(contents=mark_safe(contents), attrs=attrs) |
||
133 | |||
134 | def errors(self): |
||
135 | return mark_safe(self.field.errors.as_ul()) |
||
136 | |||
137 | class AdminReadonlyField(object): |
||
138 | def __init__(self, form, field, is_first, model_admin=None): |
||
139 | label = label_for_field(field, form._meta.model, model_admin) |
||
140 | # Make self.field look a little bit like a field. This means that
|
||
141 | # {{ field.name }} must be a useful class name to identify the field.
|
||
142 | # For convenience, store other field-related data here too.
|
||
143 | if callable(field): |
||
144 | class_name = field.__name__ != '<lambda>' and field.__name__ or '' |
||
145 | else:
|
||
146 | class_name = field |
||
147 | self.field = {
|
||
148 | 'name': class_name,
|
||
149 | 'label': label,
|
||
150 | 'field': field,
|
||
151 | 'help_text': help_text_for_field(class_name, form._meta.model)
|
||
152 | } |
||
153 | self.form = form
|
||
154 | self.model_admin = model_admin
|
||
155 | self.is_first = is_first
|
||
156 | self.is_checkbox = False |
||
157 | self.is_readonly = True |
||
158 | |||
159 | def label_tag(self): |
||
160 | attrs = {} |
||
161 | if not self.is_first: |
||
162 | attrs["class"] = "inline" |
||
163 | label = self.field['label'] |
||
164 | contents = capfirst(force_unicode(escape(label))) + u":"
|
||
165 | return mark_safe('<label%(attrs)s>%(contents)s</label>' % { |
||
166 | "attrs": flatatt(attrs),
|
||
167 | "contents": contents,
|
||
168 | }) |
||
169 | |||
170 | def contents(self): |
||
171 | from django.contrib.admin.templatetags.admin_list import _boolean_icon |
||
172 | from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE |
||
173 | field, obj, model_admin = self.field['field'], self.form.instance, self.model_admin |
||
174 | try:
|
||
175 | f, attr, value = lookup_field(field, obj, model_admin) |
||
176 | except (AttributeError, ValueError, ObjectDoesNotExist): |
||
177 | result_repr = EMPTY_CHANGELIST_VALUE |
||
178 | else:
|
||
179 | if f is None: |
||
180 | boolean = getattr(attr, "boolean", False) |
||
181 | if boolean:
|
||
182 | result_repr = _boolean_icon(value) |
||
183 | else:
|
||
184 | result_repr = smart_unicode(value) |
||
185 | if getattr(attr, "allow_tags", False): |
||
186 | result_repr = mark_safe(result_repr) |
||
187 | else:
|
||
188 | if value is None: |
||
189 | result_repr = EMPTY_CHANGELIST_VALUE |
||
190 | elif isinstance(f.rel, ManyToManyRel): |
||
191 | result_repr = ", ".join(map(unicode, value.all())) |
||
192 | else:
|
||
193 | result_repr = display_for_field(value, f) |
||
194 | return conditional_escape(result_repr)
|
||
195 | |||
196 | class InlineAdminFormSet(object): |
||
197 | """
|
||
198 | A wrapper around an inline formset for use in the admin system.
|
||
199 | """
|
||
200 | def __init__(self, inline, formset, fieldsets, prepopulated_fields=None, |
||
201 | readonly_fields=None, model_admin=None): |
||
202 | self.opts = inline
|
||
203 | self.formset = formset
|
||
204 | self.fieldsets = fieldsets
|
||
205 | self.model_admin = model_admin
|
||
206 | if readonly_fields is None: |
||
207 | readonly_fields = () |
||
208 | self.readonly_fields = readonly_fields
|
||
209 | if prepopulated_fields is None: |
||
210 | prepopulated_fields = {} |
||
211 | self.prepopulated_fields = prepopulated_fields
|
||
212 | |||
213 | def __iter__(self): |
||
214 | for form, original in zip(self.formset.initial_forms, self.formset.get_queryset()): |
||
215 | yield InlineAdminForm(self.formset, form, self.fieldsets, |
||
216 | self.prepopulated_fields, original, self.readonly_fields, |
||
217 | model_admin=self.opts)
|
||
218 | for form in self.formset.extra_forms: |
||
219 | yield InlineAdminForm(self.formset, form, self.fieldsets, |
||
220 | self.prepopulated_fields, None, self.readonly_fields, |
||
221 | model_admin=self.opts)
|
||
222 | yield InlineAdminForm(self.formset, self.formset.empty_form, |
||
223 | self.fieldsets, self.prepopulated_fields, None, |
||
224 | self.readonly_fields, model_admin=self.opts) |
||
225 | |||
226 | def fields(self): |
||
227 | fk = getattr(self.formset, "fk", None) |
||
228 | for i, field in enumerate(flatten_fieldsets(self.fieldsets)): |
||
229 | if fk and fk.name == field: |
||
230 | continue
|
||
231 | if field in self.readonly_fields: |
||
232 | yield {
|
||
233 | 'label': label_for_field(field, self.opts.model, self.opts), |
||
234 | 'widget': {
|
||
235 | 'is_hidden': False |
||
236 | }, |
||
237 | 'required': False |
||
238 | } |
||
239 | else:
|
||
240 | yield self.formset.form.base_fields[field] |
||
241 | |||
242 | def _media(self): |
||
243 | media = self.opts.media + self.formset.media |
||
244 | for fs in self: |
||
245 | media = media + fs.media |
||
246 | return media
|
||
247 | media = property(_media)
|
||
248 | |||
249 | class InlineAdminForm(AdminForm): |
||
250 | """
|
||
251 | A wrapper around an inline form for use in the admin system.
|
||
252 | """
|
||
253 | def __init__(self, formset, form, fieldsets, prepopulated_fields, original, |
||
254 | readonly_fields=None, model_admin=None): |
||
255 | self.formset = formset
|
||
256 | self.model_admin = model_admin
|
||
257 | self.original = original
|
||
258 | if original is not None: |
||
259 | self.original_content_type_id = ContentType.objects.get_for_model(original).pk
|
||
260 | self.show_url = original and hasattr(original, 'get_absolute_url') |
||
261 | super(InlineAdminForm, self).__init__(form, fieldsets, prepopulated_fields, |
||
262 | readonly_fields, model_admin) |
||
263 | |||
264 | def __iter__(self): |
||
265 | for name, options in self.fieldsets: |
||
266 | yield InlineFieldset(self.formset, self.form, name, |
||
267 | self.readonly_fields, model_admin=self.model_admin, **options) |
||
268 | |||
269 | def has_auto_field(self): |
||
270 | if self.form._meta.model._meta.has_auto_field: |
||
271 | return True |
||
272 | # Also search any parents for an auto field.
|
||
273 | for parent in self.form._meta.model._meta.get_parent_list(): |
||
274 | if parent._meta.has_auto_field:
|
||
275 | return True |
||
276 | return False |
||
277 | |||
278 | def field_count(self): |
||
279 | # tabular.html uses this function for colspan value.
|
||
280 | num_of_fields = 0
|
||
281 | if self.has_auto_field(): |
||
282 | num_of_fields += 1
|
||
283 | num_of_fields += len(self.fieldsets[0][1]["fields"]) |
||
284 | if self.formset.can_order: |
||
285 | num_of_fields += 1
|
||
286 | if self.formset.can_delete: |
||
287 | num_of_fields += 1
|
||
288 | return num_of_fields
|
||
289 | |||
290 | def pk_field(self): |
||
291 | return AdminField(self.form, self.formset._pk_field.name, False) |
||
292 | |||
293 | def fk_field(self): |
||
294 | fk = getattr(self.formset, "fk", None) |
||
295 | if fk:
|
||
296 | return AdminField(self.form, fk.name, False) |
||
297 | else:
|
||
298 | return "" |
||
299 | |||
300 | def deletion_field(self): |
||
301 | from django.forms.formsets import DELETION_FIELD_NAME |
||
302 | return AdminField(self.form, DELETION_FIELD_NAME, False) |
||
303 | |||
304 | def ordering_field(self): |
||
305 | from django.forms.formsets import ORDERING_FIELD_NAME |
||
306 | return AdminField(self.form, ORDERING_FIELD_NAME, False) |
||
307 | |||
308 | class InlineFieldset(Fieldset): |
||
309 | def __init__(self, formset, *args, **kwargs): |
||
310 | self.formset = formset
|
||
311 | super(InlineFieldset, self).__init__(*args, **kwargs) |
||
312 | |||
313 | def __iter__(self): |
||
314 | fk = getattr(self.formset, "fk", None) |
||
315 | for field in self.fields: |
||
316 | if fk and fk.name == field: |
||
317 | continue
|
||
318 | yield Fieldline(self.form, field, self.readonly_fields, |
||
319 | model_admin=self.model_admin)
|
||
320 | |||
321 | class AdminErrorList(forms.util.ErrorList): |
||
322 | """
|
||
323 | Stores all errors for the form/formsets in an add/change stage view.
|
||
324 | """
|
||
325 | def __init__(self, form, inline_formsets): |
||
326 | if form.is_bound:
|
||
327 | self.extend(form.errors.values())
|
||
328 | for inline_formset in inline_formsets: |
||
329 | self.extend(inline_formset.non_form_errors())
|
||
330 | for errors_in_inline_form in inline_formset.errors: |
||
331 | self.extend(errors_in_inline_form.values())
|
||
332 | |||
333 | def normalize_fieldsets(fieldsets): |
||
334 | """
|
||
335 | Make sure the keys in fieldset dictionaries are strings. Returns the
|
||
336 | normalized data.
|
||
337 | """
|
||
338 | result = [] |
||
339 | for name, options in fieldsets: |
||
340 | result.append((name, normalize_dictionary(options))) |
||
341 | return result
|
||
342 | |||
343 | def normalize_dictionary(data_dict): |
||
344 | """
|
||
345 | Converts all the keys in "data_dict" to strings. The keys must be
|
||
346 | convertible using str().
|
||
347 | """
|
||
348 | for key, value in data_dict.items(): |
||
349 | if not isinstance(key, str): |
||
350 | del data_dict[key]
|
||
351 | data_dict[str(key)] = value
|
||
352 | return data_dict |