app/django/contrib/admin/templatetags/admin_modify.py
changeset 54 03e267d67478
child 323 ff1a9aa48cfd
equal deleted inserted replaced
53:57b4279d8c4e 54:03e267d67478
       
     1 from django import template
       
     2 from django.contrib.admin.views.main import AdminBoundField
       
     3 from django.template import loader
       
     4 from django.utils.text import capfirst
       
     5 from django.utils.encoding import force_unicode
       
     6 from django.utils.safestring import mark_safe
       
     7 from django.utils.html import escape
       
     8 from django.db import models
       
     9 from django.db.models.fields import Field
       
    10 from django.db.models.related import BoundRelatedObject
       
    11 from django.conf import settings
       
    12 import re
       
    13 
       
    14 register = template.Library()
       
    15 
       
    16 word_re = re.compile('[A-Z][a-z]+')
       
    17 absolute_url_re = re.compile(r'^(?:http(?:s)?:/)?/', re.IGNORECASE)
       
    18 
       
    19 def class_name_to_underscored(name):
       
    20     return u'_'.join([s.lower() for s in word_re.findall(name)[:-1]])
       
    21 
       
    22 def include_admin_script(script_path):
       
    23     """
       
    24     Returns an HTML script element for including a script from the admin
       
    25     media url (or other location if an absolute url is given).
       
    26 
       
    27     Example usage::
       
    28 
       
    29         {% include_admin_script "js/calendar.js" %}
       
    30 
       
    31     could return::
       
    32 
       
    33         <script type="text/javascript" src="/media/admin/js/calendar.js">
       
    34     """
       
    35     if not absolute_url_re.match(script_path):
       
    36         script_path = '%s%s' % (settings.ADMIN_MEDIA_PREFIX, script_path)
       
    37     return mark_safe(u'<script type="text/javascript" src="%s"></script>'
       
    38             % script_path)
       
    39 include_admin_script = register.simple_tag(include_admin_script)
       
    40 
       
    41 def submit_row(context):
       
    42     opts = context['opts']
       
    43     change = context['change']
       
    44     is_popup = context['is_popup']
       
    45     return {
       
    46         'onclick_attrib': (opts.get_ordered_objects() and change
       
    47                             and 'onclick="submitOrderForm();"' or ''),
       
    48         'show_delete_link': (not is_popup and context['has_delete_permission']
       
    49                               and (change or context['show_delete'])),
       
    50         'show_save_as_new': not is_popup and change and opts.admin.save_as,
       
    51         'show_save_and_add_another': not is_popup and (not opts.admin.save_as or context['add']),
       
    52         'show_save_and_continue': not is_popup and context['has_change_permission'],
       
    53         'show_save': True
       
    54     }
       
    55 submit_row = register.inclusion_tag('admin/submit_line.html', takes_context=True)(submit_row)
       
    56 
       
    57 def field_label(bound_field):
       
    58     class_names = []
       
    59     if isinstance(bound_field.field, models.BooleanField):
       
    60         class_names.append("vCheckboxLabel")
       
    61         colon = ""
       
    62     else:
       
    63         if not bound_field.field.blank:
       
    64             class_names.append('required')
       
    65         if not bound_field.first:
       
    66             class_names.append('inline')
       
    67         colon = ":"
       
    68     class_str = class_names and u' class="%s"' % u' '.join(class_names) or u''
       
    69     return mark_safe(u'<label for="%s"%s>%s%s</label> ' %
       
    70             (bound_field.element_id, class_str,
       
    71             escape(force_unicode(capfirst(bound_field.field.verbose_name))),
       
    72             colon))
       
    73 field_label = register.simple_tag(field_label)
       
    74 
       
    75 class FieldWidgetNode(template.Node):
       
    76     nodelists = {}
       
    77     default = None
       
    78 
       
    79     def __init__(self, bound_field_var):
       
    80         self.bound_field_var = template.Variable(bound_field_var)
       
    81 
       
    82     def get_nodelist(cls, klass):
       
    83         if klass not in cls.nodelists:
       
    84             try:
       
    85                 field_class_name = klass.__name__
       
    86                 template_name = u"widget/%s.html" % class_name_to_underscored(field_class_name)
       
    87                 nodelist = loader.get_template(template_name).nodelist
       
    88             except template.TemplateDoesNotExist:
       
    89                 super_klass = bool(klass.__bases__) and klass.__bases__[0] or None
       
    90                 if super_klass and super_klass != Field:
       
    91                     nodelist = cls.get_nodelist(super_klass)
       
    92                 else:
       
    93                     if not cls.default:
       
    94                         cls.default = loader.get_template("widget/default.html").nodelist
       
    95                     nodelist = cls.default
       
    96 
       
    97             cls.nodelists[klass] = nodelist
       
    98             return nodelist
       
    99         else:
       
   100             return cls.nodelists[klass]
       
   101     get_nodelist = classmethod(get_nodelist)
       
   102 
       
   103     def render(self, context):
       
   104         bound_field = self.bound_field_var.resolve(context)
       
   105 
       
   106         context.push()
       
   107         context['bound_field'] = bound_field
       
   108 
       
   109         output = self.get_nodelist(bound_field.field.__class__).render(context)
       
   110         context.pop()
       
   111         return output
       
   112 
       
   113 class FieldWrapper(object):
       
   114     def __init__(self, field ):
       
   115         self.field = field
       
   116 
       
   117     def needs_header(self):
       
   118         return not isinstance(self.field, models.AutoField)
       
   119 
       
   120     def header_class_attribute(self):
       
   121         return self.field.blank and mark_safe(' class="optional"') or ''
       
   122 
       
   123     def use_raw_id_admin(self):
       
   124         return isinstance(self.field.rel, (models.ManyToOneRel, models.ManyToManyRel)) \
       
   125             and self.field.rel.raw_id_admin
       
   126 
       
   127 class FormFieldCollectionWrapper(object):
       
   128     def __init__(self, field_mapping, fields, index):
       
   129         self.field_mapping = field_mapping
       
   130         self.fields = fields
       
   131         self.bound_fields = [AdminBoundField(field, self.field_mapping, field_mapping['original'])
       
   132                              for field in self.fields]
       
   133         self.index = index
       
   134 
       
   135 class TabularBoundRelatedObject(BoundRelatedObject):
       
   136     def __init__(self, related_object, field_mapping, original):
       
   137         super(TabularBoundRelatedObject, self).__init__(related_object, field_mapping, original)
       
   138         self.field_wrapper_list = [FieldWrapper(field) for field in self.relation.editable_fields()]
       
   139 
       
   140         fields = self.relation.editable_fields()
       
   141 
       
   142         self.form_field_collection_wrappers = [FormFieldCollectionWrapper(field_mapping, fields, i)
       
   143                                                for (i,field_mapping) in self.field_mappings.items() ]
       
   144         self.original_row_needed = max([fw.use_raw_id_admin() for fw in self.field_wrapper_list])
       
   145         self.show_url = original and hasattr(self.relation.opts, 'get_absolute_url')
       
   146 
       
   147     def template_name(self):
       
   148         return "admin/edit_inline_tabular.html"
       
   149 
       
   150 class StackedBoundRelatedObject(BoundRelatedObject):
       
   151     def __init__(self, related_object, field_mapping, original):
       
   152         super(StackedBoundRelatedObject, self).__init__(related_object, field_mapping, original)
       
   153         fields = self.relation.editable_fields()
       
   154         self.field_mappings.fill()
       
   155         self.form_field_collection_wrappers = [FormFieldCollectionWrapper(field_mapping ,fields, i)
       
   156                                                for (i,field_mapping) in self.field_mappings.items()]
       
   157         self.show_url = original and hasattr(self.relation.opts, 'get_absolute_url')
       
   158 
       
   159     def template_name(self):
       
   160         return "admin/edit_inline_stacked.html"
       
   161 
       
   162 class EditInlineNode(template.Node):
       
   163     def __init__(self, rel_var):
       
   164         self.rel_var = template.Variable(rel_var)
       
   165 
       
   166     def render(self, context):
       
   167         relation = self.rel_var.resolve(context)
       
   168         context.push()
       
   169         if relation.field.rel.edit_inline == models.TABULAR:
       
   170             bound_related_object_class = TabularBoundRelatedObject
       
   171         elif relation.field.rel.edit_inline == models.STACKED:
       
   172             bound_related_object_class = StackedBoundRelatedObject
       
   173         else:
       
   174             bound_related_object_class = relation.field.rel.edit_inline
       
   175         original = context.get('original', None)
       
   176         bound_related_object = relation.bind(context['form'], original, bound_related_object_class)
       
   177         context['bound_related_object'] = bound_related_object
       
   178         t = loader.get_template(bound_related_object.template_name())
       
   179         output = t.render(context)
       
   180         context.pop()
       
   181         return output
       
   182 
       
   183 def output_all(form_fields):
       
   184     return u''.join([force_unicode(f) for f in form_fields])
       
   185 output_all = register.simple_tag(output_all)
       
   186 
       
   187 def auto_populated_field_script(auto_pop_fields, change = False):
       
   188     t = []
       
   189     for field in auto_pop_fields:
       
   190         if change:
       
   191             t.append(u'document.getElementById("id_%s")._changed = true;' % field.name)
       
   192         else:
       
   193             t.append(u'document.getElementById("id_%s").onchange = function() { this._changed = true; };' % field.name)
       
   194 
       
   195         add_values = u' + " " + '.join([u'document.getElementById("id_%s").value' % g for g in field.prepopulate_from])
       
   196         for f in field.prepopulate_from:
       
   197             t.append(u'document.getElementById("id_%s").onkeyup = function() {' \
       
   198                      ' var e = document.getElementById("id_%s");' \
       
   199                      ' if(!e._changed) { e.value = URLify(%s, %s);} }; ' % (
       
   200                      f, field.name, add_values, field.max_length))
       
   201     return mark_safe(u''.join(t))
       
   202 auto_populated_field_script = register.simple_tag(auto_populated_field_script)
       
   203 
       
   204 def filter_interface_script_maybe(bound_field):
       
   205     f = bound_field.field
       
   206     if f.rel and isinstance(f.rel, models.ManyToManyRel) and f.rel.filter_interface:
       
   207         return mark_safe(u'<script type="text/javascript">addEvent(window, "load", function(e) {' \
       
   208               ' SelectFilter.init("id_%s", "%s", %s, "%s"); });</script>\n' % (
       
   209               f.name, escape(f.verbose_name.replace('"', '\\"')), f.rel.filter_interface-1, settings.ADMIN_MEDIA_PREFIX))
       
   210     else:
       
   211         return ''
       
   212 filter_interface_script_maybe = register.simple_tag(filter_interface_script_maybe)
       
   213 
       
   214 def field_widget(parser, token):
       
   215     bits = token.contents.split()
       
   216     if len(bits) != 2:
       
   217         raise template.TemplateSyntaxError, "%s takes 1 argument" % bits[0]
       
   218     return FieldWidgetNode(bits[1])
       
   219 field_widget = register.tag(field_widget)
       
   220 
       
   221 def edit_inline(parser, token):
       
   222     bits = token.contents.split()
       
   223     if len(bits) != 2:
       
   224         raise template.TemplateSyntaxError, "%s takes 1 argument" % bits[0]
       
   225     return EditInlineNode(bits[1])
       
   226 edit_inline = register.tag(edit_inline)
       
   227 
       
   228 def admin_field_line(context, argument_val):
       
   229     if isinstance(argument_val, AdminBoundField):
       
   230         bound_fields = [argument_val]
       
   231     else:
       
   232         bound_fields = [bf for bf in argument_val]
       
   233     add = context['add']
       
   234     change = context['change']
       
   235 
       
   236     class_names = ['form-row']
       
   237     for bound_field in bound_fields:
       
   238         for f in bound_field.form_fields:
       
   239             if f.errors():
       
   240                 class_names.append('errors')
       
   241                 break
       
   242 
       
   243     # Assumes BooleanFields won't be stacked next to each other!
       
   244     if isinstance(bound_fields[0].field, models.BooleanField):
       
   245         class_names.append('checkbox-row')
       
   246 
       
   247     return {
       
   248         'add': context['add'],
       
   249         'change': context['change'],
       
   250         'bound_fields': bound_fields,
       
   251         'class_names': " ".join(class_names),
       
   252     }
       
   253 admin_field_line = register.inclusion_tag('admin/field_line.html', takes_context=True)(admin_field_line)