web/lib/django/contrib/admin/helpers.py
changeset 29 cc9b7e14412b
parent 0 0d40e90630ef
equal deleted inserted replaced
28:b758351d191f 29:cc9b7e14412b
     1 
       
     2 from django import forms
     1 from django import forms
     3 from django.conf import settings
     2 from django.conf import settings
     4 from django.utils.html import escape
     3 from django.contrib.admin.util import flatten_fieldsets, lookup_field
       
     4 from django.contrib.admin.util import display_for_field, label_for_field
       
     5 from django.contrib.contenttypes.models import ContentType
       
     6 from django.core.exceptions import ObjectDoesNotExist
       
     7 from django.db.models.fields import FieldDoesNotExist
       
     8 from django.db.models.fields.related import ManyToManyRel
       
     9 from django.forms.util import flatatt
       
    10 from django.template.defaultfilters import capfirst
       
    11 from django.utils.encoding import force_unicode, smart_unicode
       
    12 from django.utils.html import escape, conditional_escape
     5 from django.utils.safestring import mark_safe
    13 from django.utils.safestring import mark_safe
     6 from django.utils.encoding import force_unicode
       
     7 from django.contrib.admin.util import flatten_fieldsets
       
     8 from django.contrib.contenttypes.models import ContentType
       
     9 from django.utils.translation import ugettext_lazy as _
    14 from django.utils.translation import ugettext_lazy as _
       
    15 
    10 
    16 
    11 ACTION_CHECKBOX_NAME = '_selected_action'
    17 ACTION_CHECKBOX_NAME = '_selected_action'
    12 
    18 
    13 class ActionForm(forms.Form):
    19 class ActionForm(forms.Form):
    14     action = forms.ChoiceField(label=_('Action:'))
    20     action = forms.ChoiceField(label=_('Action:'))
       
    21     select_across = forms.BooleanField(label='', required=False, initial=0,
       
    22         widget=forms.HiddenInput({'class': 'select-across'}))
    15 
    23 
    16 checkbox = forms.CheckboxInput({'class': 'action-select'}, lambda value: False)
    24 checkbox = forms.CheckboxInput({'class': 'action-select'}, lambda value: False)
    17 
    25 
    18 class AdminForm(object):
    26 class AdminForm(object):
    19     def __init__(self, form, fieldsets, prepopulated_fields):
    27     def __init__(self, form, fieldsets, prepopulated_fields, readonly_fields=None, model_admin=None):
    20         self.form, self.fieldsets = form, normalize_fieldsets(fieldsets)
    28         self.form, self.fieldsets = form, normalize_fieldsets(fieldsets)
    21         self.prepopulated_fields = [{
    29         self.prepopulated_fields = [{
    22             'field': form[field_name],
    30             'field': form[field_name],
    23             'dependencies': [form[f] for f in dependencies]
    31             'dependencies': [form[f] for f in dependencies]
    24         } for field_name, dependencies in prepopulated_fields.items()]
    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
    25 
    37 
    26     def __iter__(self):
    38     def __iter__(self):
    27         for name, options in self.fieldsets:
    39         for name, options in self.fieldsets:
    28             yield Fieldset(self.form, name, **options)
    40             yield Fieldset(self.form, name,
       
    41                 readonly_fields=self.readonly_fields,
       
    42                 model_admin=self.model_admin,
       
    43                 **options
       
    44             )
    29 
    45 
    30     def first_field(self):
    46     def first_field(self):
    31         try:
    47         try:
    32             fieldset_name, fieldset_options = self.fieldsets[0]
    48             fieldset_name, fieldset_options = self.fieldsets[0]
    33             field_name = fieldset_options['fields'][0]
    49             field_name = fieldset_options['fields'][0]
    47             media = media + fs.media
    63             media = media + fs.media
    48         return media
    64         return media
    49     media = property(_media)
    65     media = property(_media)
    50 
    66 
    51 class Fieldset(object):
    67 class Fieldset(object):
    52     def __init__(self, form, name=None, fields=(), classes=(), description=None):
    68     def __init__(self, form, name=None, readonly_fields=(), fields=(), classes=(),
       
    69       description=None, model_admin=None):
    53         self.form = form
    70         self.form = form
    54         self.name, self.fields = name, fields
    71         self.name, self.fields = name, fields
    55         self.classes = u' '.join(classes)
    72         self.classes = u' '.join(classes)
    56         self.description = description
    73         self.description = description
       
    74         self.model_admin = model_admin
       
    75         self.readonly_fields = readonly_fields
    57 
    76 
    58     def _media(self):
    77     def _media(self):
    59         if 'collapse' in self.classes:
    78         if 'collapse' in self.classes:
    60             return forms.Media(js=['%sjs/admin/CollapsedFieldsets.js' % settings.ADMIN_MEDIA_PREFIX])
    79             js = ['js/jquery.min.js', 'js/jquery.init.js', 'js/collapse.min.js']
       
    80             return forms.Media(js=['%s%s' % (settings.ADMIN_MEDIA_PREFIX, url) for url in js])
    61         return forms.Media()
    81         return forms.Media()
    62     media = property(_media)
    82     media = property(_media)
    63 
    83 
    64     def __iter__(self):
    84     def __iter__(self):
    65         for field in self.fields:
    85         for field in self.fields:
    66             yield Fieldline(self.form, field)
    86             yield Fieldline(self.form, field, self.readonly_fields, model_admin=self.model_admin)
    67 
    87 
    68 class Fieldline(object):
    88 class Fieldline(object):
    69     def __init__(self, form, field):
    89     def __init__(self, form, field, readonly_fields=None, model_admin=None):
    70         self.form = form # A django.forms.Form instance
    90         self.form = form # A django.forms.Form instance
    71         if isinstance(field, basestring):
    91         if not hasattr(field, "__iter__"):
    72             self.fields = [field]
    92             self.fields = [field]
    73         else:
    93         else:
    74             self.fields = field
    94             self.fields = field
       
    95         self.model_admin = model_admin
       
    96         if readonly_fields is None:
       
    97             readonly_fields = ()
       
    98         self.readonly_fields = readonly_fields
    75 
    99 
    76     def __iter__(self):
   100     def __iter__(self):
    77         for i, field in enumerate(self.fields):
   101         for i, field in enumerate(self.fields):
    78             yield AdminField(self.form, field, is_first=(i == 0))
   102             if field in self.readonly_fields:
       
   103                 yield AdminReadonlyField(self.form, field, is_first=(i == 0),
       
   104                     model_admin=self.model_admin)
       
   105             else:
       
   106                 yield AdminField(self.form, field, is_first=(i == 0))
    79 
   107 
    80     def errors(self):
   108     def errors(self):
    81         return mark_safe(u'\n'.join([self.form[f].errors.as_ul() for f in self.fields]).strip('\n'))
   109         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'))
    82 
   110 
    83 class AdminField(object):
   111 class AdminField(object):
    84     def __init__(self, form, field, is_first):
   112     def __init__(self, form, field, is_first):
    85         self.field = form[field] # A django.forms.BoundField instance
   113         self.field = form[field] # A django.forms.BoundField instance
    86         self.is_first = is_first # Whether this field is first on the line
   114         self.is_first = is_first # Whether this field is first on the line
    98         if not self.is_first:
   126         if not self.is_first:
    99             classes.append(u'inline')
   127             classes.append(u'inline')
   100         attrs = classes and {'class': u' '.join(classes)} or {}
   128         attrs = classes and {'class': u' '.join(classes)} or {}
   101         return self.field.label_tag(contents=contents, attrs=attrs)
   129         return self.field.label_tag(contents=contents, attrs=attrs)
   102 
   130 
       
   131 class AdminReadonlyField(object):
       
   132     def __init__(self, form, field, is_first, model_admin=None):
       
   133         label = label_for_field(field, form._meta.model, model_admin)
       
   134         # Make self.field look a little bit like a field. This means that
       
   135         # {{ field.name }} must be a useful class name to identify the field.
       
   136         # For convenience, store other field-related data here too.
       
   137         if callable(field):
       
   138             class_name = field.__name__ != '<lambda>' and field.__name__ or ''
       
   139         else:
       
   140             class_name = field
       
   141         self.field = {
       
   142             'name': class_name,
       
   143             'label': label,
       
   144             'field': field,
       
   145         }
       
   146         self.form = form
       
   147         self.model_admin = model_admin
       
   148         self.is_first = is_first
       
   149         self.is_checkbox = False
       
   150         self.is_readonly = True
       
   151 
       
   152     def label_tag(self):
       
   153         attrs = {}
       
   154         if not self.is_first:
       
   155             attrs["class"] = "inline"
       
   156         label = self.field['label']
       
   157         contents = capfirst(force_unicode(escape(label))) + u":"
       
   158         return mark_safe('<label%(attrs)s>%(contents)s</label>' % {
       
   159             "attrs": flatatt(attrs),
       
   160             "contents": contents,
       
   161         })
       
   162 
       
   163     def contents(self):
       
   164         from django.contrib.admin.templatetags.admin_list import _boolean_icon
       
   165         from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE
       
   166         field, obj, model_admin = self.field['field'], self.form.instance, self.model_admin
       
   167         try:
       
   168             f, attr, value = lookup_field(field, obj, model_admin)
       
   169         except (AttributeError, ValueError, ObjectDoesNotExist):
       
   170             result_repr = EMPTY_CHANGELIST_VALUE
       
   171         else:
       
   172             if f is None:
       
   173                 boolean = getattr(attr, "boolean", False)
       
   174                 if boolean:
       
   175                     result_repr = _boolean_icon(value)
       
   176                 else:
       
   177                     result_repr = smart_unicode(value)
       
   178                     if getattr(attr, "allow_tags", False):
       
   179                         result_repr = mark_safe(result_repr)
       
   180             else:
       
   181                 if value is None:
       
   182                     result_repr = EMPTY_CHANGELIST_VALUE
       
   183                 elif isinstance(f.rel, ManyToManyRel):
       
   184                     result_repr = ", ".join(map(unicode, value.all()))
       
   185                 else:
       
   186                     result_repr = display_for_field(value, f)
       
   187         return conditional_escape(result_repr)
       
   188 
   103 class InlineAdminFormSet(object):
   189 class InlineAdminFormSet(object):
   104     """
   190     """
   105     A wrapper around an inline formset for use in the admin system.
   191     A wrapper around an inline formset for use in the admin system.
   106     """
   192     """
   107     def __init__(self, inline, formset, fieldsets):
   193     def __init__(self, inline, formset, fieldsets, readonly_fields=None, model_admin=None):
   108         self.opts = inline
   194         self.opts = inline
   109         self.formset = formset
   195         self.formset = formset
   110         self.fieldsets = fieldsets
   196         self.fieldsets = fieldsets
       
   197         self.model_admin = model_admin
       
   198         if readonly_fields is None:
       
   199             readonly_fields = ()
       
   200         self.readonly_fields = readonly_fields
   111 
   201 
   112     def __iter__(self):
   202     def __iter__(self):
   113         for form, original in zip(self.formset.initial_forms, self.formset.get_queryset()):
   203         for form, original in zip(self.formset.initial_forms, self.formset.get_queryset()):
   114             yield InlineAdminForm(self.formset, form, self.fieldsets, self.opts.prepopulated_fields, original)
   204             yield InlineAdminForm(self.formset, form, self.fieldsets,
       
   205                 self.opts.prepopulated_fields, original, self.readonly_fields,
       
   206                 model_admin=self.model_admin)
   115         for form in self.formset.extra_forms:
   207         for form in self.formset.extra_forms:
   116             yield InlineAdminForm(self.formset, form, self.fieldsets, self.opts.prepopulated_fields, None)
   208             yield InlineAdminForm(self.formset, form, self.fieldsets,
       
   209                 self.opts.prepopulated_fields, None, self.readonly_fields,
       
   210                 model_admin=self.model_admin)
       
   211         yield InlineAdminForm(self.formset, self.formset.empty_form,
       
   212             self.fieldsets, self.opts.prepopulated_fields, None,
       
   213             self.readonly_fields, model_admin=self.model_admin)
   117 
   214 
   118     def fields(self):
   215     def fields(self):
   119         fk = getattr(self.formset, "fk", None)
   216         fk = getattr(self.formset, "fk", None)
   120         for field_name in flatten_fieldsets(self.fieldsets):
   217         for i, field in enumerate(flatten_fieldsets(self.fieldsets)):
   121             if fk and fk.name == field_name:
   218             if fk and fk.name == field:
   122                 continue
   219                 continue
   123             yield self.formset.form.base_fields[field_name]
   220             if field in self.readonly_fields:
       
   221                 yield {
       
   222                     'label': label_for_field(field, self.opts.model, self.model_admin),
       
   223                     'widget': {
       
   224                         'is_hidden': False
       
   225                     },
       
   226                     'required': False
       
   227                 }
       
   228             else:
       
   229                 yield self.formset.form.base_fields[field]
   124 
   230 
   125     def _media(self):
   231     def _media(self):
   126         media = self.opts.media + self.formset.media
   232         media = self.opts.media + self.formset.media
   127         for fs in self:
   233         for fs in self:
   128             media = media + fs.media
   234             media = media + fs.media
   131 
   237 
   132 class InlineAdminForm(AdminForm):
   238 class InlineAdminForm(AdminForm):
   133     """
   239     """
   134     A wrapper around an inline form for use in the admin system.
   240     A wrapper around an inline form for use in the admin system.
   135     """
   241     """
   136     def __init__(self, formset, form, fieldsets, prepopulated_fields, original):
   242     def __init__(self, formset, form, fieldsets, prepopulated_fields, original,
       
   243       readonly_fields=None, model_admin=None):
   137         self.formset = formset
   244         self.formset = formset
       
   245         self.model_admin = model_admin
   138         self.original = original
   246         self.original = original
   139         if original is not None:
   247         if original is not None:
   140             self.original_content_type_id = ContentType.objects.get_for_model(original).pk
   248             self.original_content_type_id = ContentType.objects.get_for_model(original).pk
   141         self.show_url = original and hasattr(original, 'get_absolute_url')
   249         self.show_url = original and hasattr(original, 'get_absolute_url')
   142         super(InlineAdminForm, self).__init__(form, fieldsets, prepopulated_fields)
   250         super(InlineAdminForm, self).__init__(form, fieldsets, prepopulated_fields,
       
   251             readonly_fields, model_admin)
   143 
   252 
   144     def __iter__(self):
   253     def __iter__(self):
   145         for name, options in self.fieldsets:
   254         for name, options in self.fieldsets:
   146             yield InlineFieldset(self.formset, self.form, name, **options)
   255             yield InlineFieldset(self.formset, self.form, name,
       
   256                 self.readonly_fields, model_admin=self.model_admin, **options)
   147 
   257 
   148     def has_auto_field(self):
   258     def has_auto_field(self):
   149         if self.form._meta.model._meta.has_auto_field:
   259         if self.form._meta.model._meta.has_auto_field:
   150             return True
   260             return True
   151         # Also search any parents for an auto field.
   261         # Also search any parents for an auto field.
   192     def __iter__(self):
   302     def __iter__(self):
   193         fk = getattr(self.formset, "fk", None)
   303         fk = getattr(self.formset, "fk", None)
   194         for field in self.fields:
   304         for field in self.fields:
   195             if fk and fk.name == field:
   305             if fk and fk.name == field:
   196                 continue
   306                 continue
   197             yield Fieldline(self.form, field)
   307             yield Fieldline(self.form, field, self.readonly_fields,
       
   308                 model_admin=self.model_admin)
   198 
   309 
   199 class AdminErrorList(forms.util.ErrorList):
   310 class AdminErrorList(forms.util.ErrorList):
   200     """
   311     """
   201     Stores all errors for the form/formsets in an add/change stage view.
   312     Stores all errors for the form/formsets in an add/change stage view.
   202     """
   313     """
   226     for key, value in data_dict.items():
   337     for key, value in data_dict.items():
   227         if not isinstance(key, str):
   338         if not isinstance(key, str):
   228             del data_dict[key]
   339             del data_dict[key]
   229             data_dict[str(key)] = value
   340             data_dict[str(key)] = value
   230     return data_dict
   341     return data_dict
   231