PageRenderTime 72ms CodeModel.GetById 12ms app.highlight 54ms RepoModel.GetById 1ms app.codeStats 0ms

/django/contrib/admin/templatetags/admin_list.py

https://code.google.com/p/mango-py/
Python | 330 lines | 322 code | 5 blank | 3 comment | 3 complexity | 5b5e145674e5123dd65d4053e1dfb1cd MD5 | raw file
  1import datetime
  2
  3from django.conf import settings
  4from django.contrib.admin.util import lookup_field, display_for_field, label_for_field
  5from django.contrib.admin.views.main import (ALL_VAR, EMPTY_CHANGELIST_VALUE,
  6    ORDER_VAR, ORDER_TYPE_VAR, PAGE_VAR, SEARCH_VAR)
  7from django.core.exceptions import ObjectDoesNotExist
  8from django.db import models
  9from django.utils import formats
 10from django.utils.html import escape, conditional_escape
 11from django.utils.safestring import mark_safe
 12from django.utils.text import capfirst
 13from django.utils.translation import ugettext as _
 14from django.utils.encoding import smart_unicode, force_unicode
 15from django.template import Library
 16
 17
 18register = Library()
 19
 20DOT = '.'
 21
 22def paginator_number(cl,i):
 23    """
 24    Generates an individual page index link in a paginated list.
 25    """
 26    if i == DOT:
 27        return u'... '
 28    elif i == cl.page_num:
 29        return mark_safe(u'<span class="this-page">%d</span> ' % (i+1))
 30    else:
 31        return mark_safe(u'<a href="%s"%s>%d</a> ' % (escape(cl.get_query_string({PAGE_VAR: i})), (i == cl.paginator.num_pages-1 and ' class="end"' or ''), i+1))
 32paginator_number = register.simple_tag(paginator_number)
 33
 34def pagination(cl):
 35    """
 36    Generates the series of links to the pages in a paginated list.
 37    """
 38    paginator, page_num = cl.paginator, cl.page_num
 39
 40    pagination_required = (not cl.show_all or not cl.can_show_all) and cl.multi_page
 41    if not pagination_required:
 42        page_range = []
 43    else:
 44        ON_EACH_SIDE = 3
 45        ON_ENDS = 2
 46
 47        # If there are 10 or fewer pages, display links to every page.
 48        # Otherwise, do some fancy
 49        if paginator.num_pages <= 10:
 50            page_range = range(paginator.num_pages)
 51        else:
 52            # Insert "smart" pagination links, so that there are always ON_ENDS
 53            # links at either end of the list of pages, and there are always
 54            # ON_EACH_SIDE links at either end of the "current page" link.
 55            page_range = []
 56            if page_num > (ON_EACH_SIDE + ON_ENDS):
 57                page_range.extend(range(0, ON_EACH_SIDE - 1))
 58                page_range.append(DOT)
 59                page_range.extend(range(page_num - ON_EACH_SIDE, page_num + 1))
 60            else:
 61                page_range.extend(range(0, page_num + 1))
 62            if page_num < (paginator.num_pages - ON_EACH_SIDE - ON_ENDS - 1):
 63                page_range.extend(range(page_num + 1, page_num + ON_EACH_SIDE + 1))
 64                page_range.append(DOT)
 65                page_range.extend(range(paginator.num_pages - ON_ENDS, paginator.num_pages))
 66            else:
 67                page_range.extend(range(page_num + 1, paginator.num_pages))
 68
 69    need_show_all_link = cl.can_show_all and not cl.show_all and cl.multi_page
 70    return {
 71        'cl': cl,
 72        'pagination_required': pagination_required,
 73        'show_all_url': need_show_all_link and cl.get_query_string({ALL_VAR: ''}),
 74        'page_range': page_range,
 75        'ALL_VAR': ALL_VAR,
 76        '1': 1,
 77    }
 78pagination = register.inclusion_tag('admin/pagination.html')(pagination)
 79
 80def result_headers(cl):
 81    """
 82    Generates the list column headers.
 83    """
 84    lookup_opts = cl.lookup_opts
 85
 86    for i, field_name in enumerate(cl.list_display):
 87        header, attr = label_for_field(field_name, cl.model,
 88            model_admin = cl.model_admin,
 89            return_attr = True
 90        )
 91        if attr:
 92            # if the field is the action checkbox: no sorting and special class
 93            if field_name == 'action_checkbox':
 94                yield {
 95                    "text": header,
 96                    "class_attrib": mark_safe(' class="action-checkbox-column"')
 97                }
 98                continue
 99
100            # It is a non-field, but perhaps one that is sortable
101            admin_order_field = getattr(attr, "admin_order_field", None)
102            if not admin_order_field:
103                yield {"text": header}
104                continue
105
106            # So this _is_ a sortable non-field.  Go to the yield
107            # after the else clause.
108        else:
109            admin_order_field = None
110
111        th_classes = []
112        new_order_type = 'asc'
113        if field_name == cl.order_field or admin_order_field == cl.order_field:
114            th_classes.append('sorted %sending' % cl.order_type.lower())
115            new_order_type = {'asc': 'desc', 'desc': 'asc'}[cl.order_type.lower()]
116
117        yield {
118            "text": header,
119            "sortable": True,
120            "url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
121            "class_attrib": mark_safe(th_classes and ' class="%s"' % ' '.join(th_classes) or '')
122        }
123
124def _boolean_icon(field_val):
125    BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
126    return mark_safe(u'<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (settings.ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val))
127
128def items_for_result(cl, result, form):
129    """
130    Generates the actual list of data.
131    """
132    first = True
133    pk = cl.lookup_opts.pk.attname
134    for field_name in cl.list_display:
135        row_class = ''
136        try:
137            f, attr, value = lookup_field(field_name, result, cl.model_admin)
138        except (AttributeError, ObjectDoesNotExist):
139            result_repr = EMPTY_CHANGELIST_VALUE
140        else:
141            if f is None:
142                if field_name == u'action_checkbox':
143                    row_class = ' class="action-checkbox"'
144                allow_tags = getattr(attr, 'allow_tags', False)
145                boolean = getattr(attr, 'boolean', False)
146                if boolean:
147                    allow_tags = True
148                    result_repr = _boolean_icon(value)
149                else:
150                    result_repr = smart_unicode(value)
151                # Strip HTML tags in the resulting text, except if the
152                # function has an "allow_tags" attribute set to True.
153                if not allow_tags:
154                    result_repr = escape(result_repr)
155                else:
156                    result_repr = mark_safe(result_repr)
157            else:
158                if isinstance(f.rel, models.ManyToOneRel):
159                    field_val = getattr(result, f.name)
160                    if field_val is None:
161                        result_repr = EMPTY_CHANGELIST_VALUE
162                    else:
163                        result_repr = escape(field_val)
164                else:
165                    result_repr = display_for_field(value, f)
166                if isinstance(f, models.DateField)\
167                or isinstance(f, models.TimeField)\
168                or isinstance(f, models.ForeignKey):
169                    row_class = ' class="nowrap"'
170        if force_unicode(result_repr) == '':
171            result_repr = mark_safe('&nbsp;')
172        # If list_display_links not defined, add the link tag to the first field
173        if (first and not cl.list_display_links) or field_name in cl.list_display_links:
174            table_tag = {True:'th', False:'td'}[first]
175            first = False
176            url = cl.url_for_result(result)
177            # Convert the pk to something that can be used in Javascript.
178            # Problem cases are long ints (23L) and non-ASCII strings.
179            if cl.to_field:
180                attr = str(cl.to_field)
181            else:
182                attr = pk
183            value = result.serializable_value(attr)
184            result_id = repr(force_unicode(value))[1:]
185            yield mark_safe(u'<%s%s><a href="%s"%s>%s</a></%s>' % \
186                (table_tag, row_class, url, (cl.is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %s); return false;"' % result_id or ''), conditional_escape(result_repr), table_tag))
187        else:
188            # By default the fields come from ModelAdmin.list_editable, but if we pull
189            # the fields out of the form instead of list_editable custom admins
190            # can provide fields on a per request basis
191            if (form and field_name in form.fields and not (
192                    field_name == cl.model._meta.pk.name and
193                        form[cl.model._meta.pk.name].is_hidden)):
194                bf = form[field_name]
195                result_repr = mark_safe(force_unicode(bf.errors) + force_unicode(bf))
196            else:
197                result_repr = conditional_escape(result_repr)
198            yield mark_safe(u'<td%s>%s</td>' % (row_class, result_repr))
199    if form and not form[cl.model._meta.pk.name].is_hidden:
200        yield mark_safe(u'<td>%s</td>' % force_unicode(form[cl.model._meta.pk.name]))
201
202class ResultList(list):
203    # Wrapper class used to return items in a list_editable
204    # changelist, annotated with the form object for error
205    # reporting purposes. Needed to maintain backwards
206    # compatibility with existing admin templates.
207    def __init__(self, form, *items):
208        self.form = form
209        super(ResultList, self).__init__(*items)
210
211def results(cl):
212    if cl.formset:
213        for res, form in zip(cl.result_list, cl.formset.forms):
214            yield ResultList(form, items_for_result(cl, res, form))
215    else:
216        for res in cl.result_list:
217            yield ResultList(None, items_for_result(cl, res, None))
218
219def result_hidden_fields(cl):
220    if cl.formset:
221        for res, form in zip(cl.result_list, cl.formset.forms):
222            if form[cl.model._meta.pk.name].is_hidden:
223                yield mark_safe(force_unicode(form[cl.model._meta.pk.name]))
224
225def result_list(cl):
226    """
227    Displays the headers and data list together
228    """
229    return {'cl': cl,
230            'result_hidden_fields': list(result_hidden_fields(cl)),
231            'result_headers': list(result_headers(cl)),
232            'results': list(results(cl))}
233result_list = register.inclusion_tag("admin/change_list_results.html")(result_list)
234
235def date_hierarchy(cl):
236    """
237    Displays the date hierarchy for date drill-down functionality.
238    """
239    if cl.date_hierarchy:
240        field_name = cl.date_hierarchy
241        year_field = '%s__year' % field_name
242        month_field = '%s__month' % field_name
243        day_field = '%s__day' % field_name
244        field_generic = '%s__' % field_name
245        year_lookup = cl.params.get(year_field)
246        month_lookup = cl.params.get(month_field)
247        day_lookup = cl.params.get(day_field)
248
249        link = lambda d: cl.get_query_string(d, [field_generic])
250
251        if not (year_lookup or month_lookup or day_lookup):
252            # select appropriate start level
253            date_range = cl.query_set.aggregate(first=models.Min(field_name),
254                                                last=models.Max(field_name))
255            if date_range['first'] and date_range['last']:
256                if date_range['first'].year == date_range['last'].year:
257                    year_lookup = date_range['first'].year
258                    if date_range['first'].month == date_range['last'].month:
259                        month_lookup = date_range['first'].month
260
261        if year_lookup and month_lookup and day_lookup:
262            day = datetime.date(int(year_lookup), int(month_lookup), int(day_lookup))
263            return {
264                'show': True,
265                'back': {
266                    'link': link({year_field: year_lookup, month_field: month_lookup}),
267                    'title': capfirst(formats.date_format(day, 'YEAR_MONTH_FORMAT'))
268                },
269                'choices': [{'title': capfirst(formats.date_format(day, 'MONTH_DAY_FORMAT'))}]
270            }
271        elif year_lookup and month_lookup:
272            days = cl.query_set.filter(**{year_field: year_lookup, month_field: month_lookup}).dates(field_name, 'day')
273            return {
274                'show': True,
275                'back': {
276                    'link': link({year_field: year_lookup}),
277                    'title': str(year_lookup)
278                },
279                'choices': [{
280                    'link': link({year_field: year_lookup, month_field: month_lookup, day_field: day.day}),
281                    'title': capfirst(formats.date_format(day, 'MONTH_DAY_FORMAT'))
282                } for day in days]
283            }
284        elif year_lookup:
285            months = cl.query_set.filter(**{year_field: year_lookup}).dates(field_name, 'month')
286            return {
287                'show' : True,
288                'back': {
289                    'link' : link({}),
290                    'title': _('All dates')
291                },
292                'choices': [{
293                    'link': link({year_field: year_lookup, month_field: month.month}),
294                    'title': capfirst(formats.date_format(month, 'YEAR_MONTH_FORMAT'))
295                } for month in months]
296            }
297        else:
298            years = cl.query_set.dates(field_name, 'year')
299            return {
300                'show': True,
301                'choices': [{
302                    'link': link({year_field: str(year.year)}),
303                    'title': str(year.year),
304                } for year in years]
305            }
306date_hierarchy = register.inclusion_tag('admin/date_hierarchy.html')(date_hierarchy)
307
308def search_form(cl):
309    """
310    Displays a search form for searching the list.
311    """
312    return {
313        'cl': cl,
314        'show_result_count': cl.result_count != cl.full_result_count,
315        'search_var': SEARCH_VAR
316    }
317search_form = register.inclusion_tag('admin/search_form.html')(search_form)
318
319def admin_list_filter(cl, spec):
320    return {'title': spec.title(), 'choices' : list(spec.choices(cl))}
321admin_list_filter = register.inclusion_tag('admin/filter.html')(admin_list_filter)
322
323def admin_actions(context):
324    """
325    Track the number of times the action field has been rendered on the page,
326    so we know which value to use.
327    """
328    context['action_index'] = context.get('action_index', -1) + 1
329    return context
330admin_actions = register.inclusion_tag("admin/actions.html", takes_context=True)(admin_actions)