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