Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/mptt/templatetags/mptt_admin.py: 18%
111 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1import datetime
2import warnings
4from django.conf import settings
5from django.contrib.admin.templatetags.admin_list import (
6 result_headers,
7 result_hidden_fields,
8)
9from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
10from django.contrib.admin.utils import (
11 display_for_field,
12 display_for_value,
13 lookup_field,
14)
15from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist
16from django.db import models
17from django.template import Library
18from django.urls import NoReverseMatch
21try:
22 from django.utils.deprecation import RemovedInDjango20Warning
23except ImportError:
24 RemovedInDjango20Warning = RuntimeWarning
25from django.contrib.admin.templatetags.admin_list import _coerce_field_name
26from django.utils.encoding import force_str
27from django.utils.html import format_html
28from django.utils.safestring import mark_safe
29from django.utils.translation import get_language_bidi
32register = Library()
35MPTT_ADMIN_LEVEL_INDENT = getattr(settings, "MPTT_ADMIN_LEVEL_INDENT", 10)
36IS_GRAPPELLI_INSTALLED = True if "grappelli" in settings.INSTALLED_APPS else False
39###
40# Ripped from contrib.admin's (1.10) items_for_result tag.
41# The only difference is we're indenting nodes according to their level.
42def mptt_items_for_result(cl, result, form):
43 """
44 Generates the actual list of data.
45 """
47 def link_in_col(is_first, field_name, cl):
48 if cl.list_display_links is None:
49 return False
50 if is_first and not cl.list_display_links:
51 return True
52 return field_name in cl.list_display_links
54 first = True
55 pk = cl.lookup_opts.pk.attname
57 # #### MPTT ADDITION START
58 # figure out which field to indent
59 mptt_indent_field = getattr(cl.model_admin, "mptt_indent_field", None)
60 if not mptt_indent_field:
61 for field_name in cl.list_display:
62 try:
63 f = cl.lookup_opts.get_field(field_name)
64 except FieldDoesNotExist:
65 if mptt_indent_field is None and field_name != "action_checkbox":
66 mptt_indent_field = field_name
67 else:
68 # first model field, use this one
69 mptt_indent_field = field_name
70 break
72 # figure out how much to indent
73 mptt_level_indent = getattr(
74 cl.model_admin, "mptt_level_indent", MPTT_ADMIN_LEVEL_INDENT
75 )
76 # #### MPTT ADDITION END
78 for field_index, field_name in enumerate(cl.list_display):
79 # #### MPTT SUBSTITUTION START
80 empty_value_display = cl.model_admin.get_empty_value_display()
81 # #### MPTT SUBSTITUTION END
82 row_classes = ["field-%s" % _coerce_field_name(field_name, field_index)]
83 try:
84 f, attr, value = lookup_field(field_name, result, cl.model_admin)
85 except ObjectDoesNotExist:
86 result_repr = empty_value_display
87 else:
88 empty_value_display = getattr(
89 attr, "empty_value_display", empty_value_display
90 )
91 if f is None or f.auto_created:
92 if field_name == "action_checkbox":
93 row_classes = ["action-checkbox"]
94 allow_tags = getattr(attr, "allow_tags", False)
95 boolean = getattr(attr, "boolean", False)
96 # #### MPTT SUBSTITUTION START
97 result_repr = display_for_value(value, empty_value_display, boolean)
98 # #### MPTT SUBSTITUTION END
99 if allow_tags:
100 warnings.warn(
101 "Deprecated allow_tags attribute used on field {}. "
102 "Use django.utils.safestring.format_html(), "
103 "format_html_join(), or mark_safe() instead.".format(
104 field_name
105 ),
106 RemovedInDjango20Warning,
107 )
108 result_repr = mark_safe(result_repr)
109 if isinstance(value, (datetime.date, datetime.time)):
110 row_classes.append("nowrap")
111 else:
112 # #### MPTT SUBSTITUTION START
113 is_many_to_one = isinstance(f.remote_field, models.ManyToOneRel)
114 if is_many_to_one:
115 # #### MPTT SUBSTITUTION END
116 field_val = getattr(result, f.name)
117 if field_val is None:
118 result_repr = empty_value_display
119 else:
120 result_repr = field_val
121 else:
122 # #### MPTT SUBSTITUTION START
123 result_repr = display_for_field(value, f, empty_value_display)
124 # #### MPTT SUBSTITUTION END
125 if isinstance(
126 f, (models.DateField, models.TimeField, models.ForeignKey)
127 ):
128 row_classes.append("nowrap")
129 if force_str(result_repr) == "":
130 result_repr = mark_safe(" ")
131 row_class = mark_safe(' class="%s"' % " ".join(row_classes))
133 # #### MPTT ADDITION START
134 if field_name == mptt_indent_field:
135 level = getattr(result, result._mptt_meta.level_attr)
136 padding_attr = mark_safe(
137 ' style="padding-%s:%spx"'
138 % (
139 "right" if get_language_bidi() else "left",
140 8 + mptt_level_indent * level,
141 )
142 )
143 else:
144 padding_attr = ""
145 # #### MPTT ADDITION END
147 # If list_display_links not defined, add the link tag to the first field
148 if link_in_col(first, field_name, cl):
149 table_tag = "th" if first else "td"
150 first = False
152 # Display link to the result's change_view if the url exists, else
153 # display just the result's representation.
154 try:
155 url = cl.url_for_result(result)
156 except NoReverseMatch:
157 link_or_text = result_repr
158 else:
159 url = add_preserved_filters(
160 {"preserved_filters": cl.preserved_filters, "opts": cl.opts},
161 url,
162 )
163 # Convert the pk to something that can be used in Javascript.
164 # Problem cases are long ints (23L) and non-ASCII strings.
165 if cl.to_field:
166 attr = str(cl.to_field)
167 else:
168 attr = pk
169 value = result.serializable_value(attr)
170 if cl.is_popup:
171 opener = format_html(' data-popup-opener="{}"', value)
172 else:
173 opener = ""
174 link_or_text = format_html(
175 '<a href="{}"{}>{}</a>', url, opener, result_repr
176 )
178 # #### MPTT SUBSTITUTION START
179 yield format_html(
180 "<{}{}{}>{}</{}>",
181 table_tag,
182 row_class,
183 padding_attr,
184 link_or_text,
185 table_tag,
186 )
187 # #### MPTT SUBSTITUTION END
188 else:
189 # By default the fields come from ModelAdmin.list_editable, but if we pull
190 # the fields out of the form instead of list_editable custom admins
191 # can provide fields on a per request basis
192 if (
193 form
194 and field_name in form.fields
195 and not (
196 field_name == cl.model._meta.pk.name
197 and form[cl.model._meta.pk.name].is_hidden
198 )
199 ):
200 bf = form[field_name]
201 result_repr = mark_safe(force_str(bf.errors) + force_str(bf))
202 # #### MPTT SUBSTITUTION START
203 yield format_html("<td{}{}>{}</td>", row_class, padding_attr, result_repr)
204 # #### MPTT SUBSTITUTION END
205 if form and not form[cl.model._meta.pk.name].is_hidden:
206 yield format_html("<td>{}</td>", force_str(form[cl.model._meta.pk.name]))
209def mptt_results(cl):
210 if cl.formset:
211 for res, form in zip(cl.result_list, cl.formset.forms):
212 yield list(mptt_items_for_result(cl, res, form))
213 else:
214 for res in cl.result_list:
215 yield list(mptt_items_for_result(cl, res, None))
218def mptt_result_list(cl):
219 """
220 Displays the headers and data list together
221 """
222 return {
223 "cl": cl,
224 "result_hidden_fields": list(result_hidden_fields(cl)),
225 "result_headers": list(result_headers(cl)),
226 "results": list(mptt_results(cl)),
227 }
230# custom template is merely so we can strip out sortable-ness from the column headers
231# Based on admin/change_list_results.html (1.3.1)
232if IS_GRAPPELLI_INSTALLED: 232 ↛ 233line 232 didn't jump to line 233, because the condition on line 232 was never true
233 mptt_result_list = register.inclusion_tag(
234 "admin/grappelli_mptt_change_list_results.html"
235 )(mptt_result_list)
236else:
237 mptt_result_list = register.inclusion_tag("admin/mptt_change_list_results.html")(
238 mptt_result_list
239 )