Skip to content

Commit

Permalink
Merge pull request #491 from ataylor32/mptt_items_for_result
Browse files Browse the repository at this point in the history
Updated the "mptt_items_for_result" function
  • Loading branch information
craigds committed Aug 6, 2016
2 parents 03be4ce + 36b8c25 commit 1c83b96
Showing 1 changed file with 113 additions and 55 deletions.
168 changes: 113 additions & 55 deletions mptt/templatetags/mptt_admin.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
from __future__ import unicode_literals

import datetime
import warnings

from django.conf import settings
from django.contrib.admin.templatetags.admin_list import (
result_hidden_fields, _boolean_icon, result_headers)
from django.contrib.admin.utils import lookup_field, display_for_field
result_hidden_fields, result_headers)
from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
from django.contrib.admin.utils import (
display_for_field, display_for_value, lookup_field)
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.template import Library
from django.utils.encoding import smart_text, force_text
from django.utils.html import escape, escapejs, conditional_escape
from django.core.urlresolvers import NoReverseMatch
from django.utils.deprecation import RemovedInDjango20Warning
from django.utils.encoding import force_text
from django.utils.html import format_html
from django.utils.safestring import mark_safe
from django.utils.translation import get_language_bidi

Expand All @@ -20,6 +27,20 @@
IS_GRAPPELLI_INSTALLED = True if 'grappelli' in settings.INSTALLED_APPS else False


###
# Ripped from contrib.admin (1.10)
def _coerce_field_name(field_name, field_index):
"""
Coerce a field_name (which may be a callable) to a string.
"""
if callable(field_name):
if field_name.__name__ == '<lambda>':
return 'lambda' + str(field_index)
else:
return field_name.__name__
return field_name


def get_empty_value_display(cl):
if hasattr(cl.model_admin, 'get_empty_value_display'):
return cl.model_admin.get_empty_value_display()
Expand All @@ -30,12 +51,20 @@ def get_empty_value_display(cl):


###
# Ripped from contrib.admin's (1.3.1) items_for_result tag.
# Ripped from contrib.admin's (1.10) items_for_result tag.
# The only difference is we're indenting nodes according to their level.
def mptt_items_for_result(cl, result, form):
"""
Generates the actual list of data.
"""

def link_in_col(is_first, field_name, cl):
if cl.list_display_links is None:
return False
if is_first and not cl.list_display_links:
return True
return field_name in cl.list_display_links

first = True
pk = cl.lookup_opts.pk.attname

Expand All @@ -54,88 +83,119 @@ def mptt_items_for_result(cl, result, form):
# first model field, use this one
mptt_indent_field = field_name
break
# #### MPTT ADDITION END

# figure out how much to indent
mptt_level_indent = getattr(cl.model_admin, 'mptt_level_indent', MPTT_ADMIN_LEVEL_INDENT)
# #### MPTT ADDITION END

for field_name in cl.list_display:
row_class = ''
for field_index, field_name in enumerate(cl.list_display):
# #### MPTT SUBSTITUTION START
empty_value_display = get_empty_value_display(cl)
# #### MPTT SUBSTITUTION END
row_classes = ['field-%s' % _coerce_field_name(field_name, field_index)]
try:
f, attr, value = lookup_field(field_name, result, cl.model_admin)
except (AttributeError, ObjectDoesNotExist):
result_repr = get_empty_value_display(cl)
except ObjectDoesNotExist:
result_repr = empty_value_display
else:
if f is None:
empty_value_display = getattr(attr, 'empty_value_display', empty_value_display)
if f is None or f.auto_created:
if field_name == 'action_checkbox':
row_class = ' class="action-checkbox"'
row_classes = ['action-checkbox']
allow_tags = getattr(attr, 'allow_tags', False)
boolean = getattr(attr, 'boolean', False)
if boolean:
allow_tags = True
result_repr = _boolean_icon(value)
else:
result_repr = smart_text(value)
# Strip HTML tags in the resulting text, except if the
# function has an "allow_tags" attribute set to True.
if not allow_tags:
result_repr = conditional_escape(result_repr)
else:
# #### MPTT SUBSTITUTION START
try:
# Changed in Django 1.9, now takes 3 arguments
result_repr = display_for_value(
value, empty_value_display, boolean)
except TypeError:
result_repr = display_for_value(value, boolean)
# #### MPTT SUBSTITUTION END
if allow_tags:
warnings.warn(
"Deprecated allow_tags attribute used on field {}. "
"Use django.utils.safestring.format_html(), "
"format_html_join(), or mark_safe() instead.".format(field_name),
RemovedInDjango20Warning
)
result_repr = mark_safe(result_repr)
if isinstance(value, (datetime.date, datetime.time)):
row_classes.append('nowrap')
else:
if isinstance(f.rel, models.ManyToOneRel):
# #### MPTT SUBSTITUTION START
try:
is_many_to_one = isinstance(
f.remote_field, models.ManyToOneRel)
except AttributeError:
is_many_to_one = isinstance(f.rel, models.ManyToOneRel)
if is_many_to_one:
# #### MPTT SUBSTITUTION END
field_val = getattr(result, f.name)
if field_val is None:
result_repr = get_empty_value_display(cl)
result_repr = empty_value_display
else:
result_repr = escape(field_val)
result_repr = field_val
else:
# #### MPTT SUBSTITUTION START
try:
result_repr = display_for_field(value, f)
except TypeError:
# Changed in Django 1.9, now takes 3 arguments
result_repr = display_for_field(
value, f, get_empty_value_display(cl))

if isinstance(f, models.DateField)\
or isinstance(f, models.TimeField)\
or isinstance(f, models.ForeignKey):
row_class = ' class="nowrap"'
value, f, empty_value_display)
# #### MPTT SUBSTITUTION END
if isinstance(f, (models.DateField, models.TimeField, models.ForeignKey)):
row_classes.append('nowrap')
if force_text(result_repr) == '':
result_repr = mark_safe('&nbsp;')
row_class = mark_safe(' class="%s"' % ' '.join(row_classes))

# #### MPTT ADDITION START
if field_name == mptt_indent_field:
level = getattr(result, result._mptt_meta.level_attr)
padding_attr = ' style="padding-%s:%spx"' % (
padding_attr = mark_safe(' style="padding-%s:%spx"' % (
'right' if get_language_bidi() else 'left',
8 + mptt_level_indent * level)
8 + mptt_level_indent * level))
else:
padding_attr = ''
# #### MPTT ADDITION END

# If list_display_links not defined, add the link tag to the first field
if (first and not cl.list_display_links) or field_name in cl.list_display_links:
table_tag = {True: 'th', False: 'td'}[first]
if link_in_col(first, field_name, cl):
table_tag = 'th' if first else 'td'
first = False
url = cl.url_for_result(result)
# Convert the pk to something that can be used in Javascript.
# Problem cases are long ints (23L) and non-ASCII strings.
if cl.to_field:
attr = str(cl.to_field)

# Display link to the result's change_view if the url exists, else
# display just the result's representation.
try:
url = cl.url_for_result(result)
except NoReverseMatch:
link_or_text = result_repr
else:
attr = pk
value = result.serializable_value(attr)
result_id = escapejs(value)
url = add_preserved_filters({'preserved_filters': cl.preserved_filters, 'opts': cl.opts}, url)
# Convert the pk to something that can be used in Javascript.
# Problem cases are long ints (23L) and non-ASCII strings.
if cl.to_field:
attr = str(cl.to_field)
else:
attr = pk
value = result.serializable_value(attr)
link_or_text = format_html(
'<a href="{}"{}>{}</a>',
url,
format_html(
' data-popup-opener="{}"', value
) if cl.is_popup else '',
result_repr)

# #### MPTT SUBSTITUTION START
yield mark_safe('<%s%s%s><a href="%s"%s>%s</a></%s>' % (
table_tag,
row_class,
padding_attr,
url,
(cl.is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, &#39;%s&#39;); return false;"' % result_id or ''), # noqa
conditional_escape(result_repr),
table_tag))
yield format_html('<{}{}{}>{}</{}>',
table_tag,
row_class,
padding_attr,
link_or_text,
table_tag)
# #### MPTT SUBSTITUTION END
else:
# By default the fields come from ModelAdmin.list_editable, but if we pull
Expand All @@ -146,13 +206,11 @@ def mptt_items_for_result(cl, result, form):
form[cl.model._meta.pk.name].is_hidden)):
bf = form[field_name]
result_repr = mark_safe(force_text(bf.errors) + force_text(bf))
else:
result_repr = conditional_escape(result_repr)
# #### MPTT SUBSTITUTION START
yield mark_safe('<td%s%s>%s</td>' % (row_class, padding_attr, result_repr))
yield format_html('<td{}{}>{}</td>', row_class, padding_attr, result_repr)
# #### MPTT SUBSTITUTION END
if form and not form[cl.model._meta.pk.name].is_hidden:
yield mark_safe('<td>%s</td>' % force_text(form[cl.model._meta.pk.name]))
yield format_html('<td>{}</td>', force_text(form[cl.model._meta.pk.name]))


def mptt_results(cl):
Expand Down

0 comments on commit 1c83b96

Please sign in to comment.