Statistics
| Branch: | Revision:

root / env / lib / python2.7 / site-packages / django / contrib / admin / helpers.py @ 1a305335

History | View | Annotate | Download (13.3 KB)

1
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